diff options
author | Fox Caminiti <fox@foxcam.net> | 2022-09-29 20:58:54 -0400 |
---|---|---|
committer | Fox Caminiti <fox@foxcam.net> | 2022-09-29 20:58:54 -0400 |
commit | 3b8bd135662d99506e8a2ebb30b0d46b57861f74 (patch) | |
tree | 533884d6ada9a14e711fa70f2e4526f247cd6034 | |
parent | 02d7df95cfc2402f0488f66f1dc5fa84cae00934 (diff) |
rewritten graph ui start
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | createcalls.cpp | 4 | ||||
-rw-r--r-- | main.cpp | 13 | ||||
-rw-r--r-- | main.h | 13 | ||||
-rw-r--r-- | my_imgui_widgets.cpp | 1286 | ||||
-rw-r--r-- | my_math.h | 12 |
6 files changed, 898 insertions, 431 deletions
@@ -1,3 +1,4 @@ bin/* compile_commands.json build_ops +imgui.ini diff --git a/createcalls.cpp b/createcalls.cpp index 5467f18..91ae66a 100644 --- a/createcalls.cpp +++ b/createcalls.cpp @@ -294,6 +294,10 @@ LoadTestFootage(project_data *File, project_state *State, memory *Memory) Source_Generate(File, State, Memory, SourceString); Layer_CreateFromSource(File, State, Memory, &File->Source[0]); + Keyframe_Insert(&File->Layer[0]->x, Memory, 00, 00); + Keyframe_Insert(&File->Layer[0]->x, Memory, 05, 05); + Keyframe_Insert(&File->Layer[0]->x, Memory, 10, 10); + File->Layer[0]->x.IsToggled = true; SelectLayer(File->Layer[0], State, 0); // AddEffect(File->Layer[0], Memory, 1); // property_channel *Property = &File->Layer[0]->x; @@ -152,11 +152,11 @@ int main(int argc, char *argv[]) { project_data File = {}; File.Width = 1920; File.Height = 1080; - File.NumberOfFrames = 65; - File.FPS = 30; + File.NumberOfFrames = 120; + File.FPS = 24; File.CurrentFrame = 1; File.StartFrame = 0; - File.EndFrame = 65; + File.EndFrame = 100; #if DEBUG @@ -301,8 +301,8 @@ int main(int argc, char *argv[]) { // I'm loading the window positions from this convenient tool. ImGui by // default saves window position to an external .ini file, which can be // loaded from disk or memory. - io.IniFilename = NULL; - ImGui::LoadIniSettingsFromMemory(ImGuiPrefs); + // io.IniFilename = NULL; + // ImGui::LoadIniSettingsFromMemory(ImGuiPrefs); ImGui::StyleColorsDark(); @@ -360,11 +360,12 @@ int main(int argc, char *argv[]) { ImGui_PropertiesPanel(&File, &State, &UI, &Memory, io); ImGui_Timeline(&File, &State, &Memory, &UI, io); + // ImGui_Graph(&File, &State, &Memory, &UI, io); #if DEBUG ImGui_DebugUndoTree(&File, &Memory); if (Debug.ToggleWindow) { - // ImGui::ShowDemoWindow(); + ImGui::ShowDemoWindow(); ImGui_DebugMemoryViewer(&File, &Memory); } #endif @@ -483,7 +483,16 @@ struct ui_graph { struct ui { real32 TimelineSplit = 600; + real32 GraphPropsSplit = 200; real32 TimelineZoom; + real32 GraphZoom = 30; + + // Under 1 is zoomed in! + real32 TimelinePercentZoomed = 1.0f; + real32 TimelinePercentOffset = 0.0f; + + real32 Y_TimelinePercentZoomed = 1.0f; + real32 Y_TimelinePercentOffset = 0.0f; // Note that I don't use "zoom" to mean the scale in relation to the // original (i.e. default = 1.0f); it's the literal screen size in pixels @@ -499,6 +508,10 @@ struct ui real32 ScrollXOffset; real32 ScrollYOffset; + // Custom scrolling for the timeline, as ImGui's didn't work well + real32 G_ScrollXOffset; + real32 G_ScrollYOffset; + // NOTE(fox): Keeping track of mouse delta myself since the ImGui threshold // dragging API doesn't let you do things like subtract the delta easily. real32 DraggingKeyframeThreshold; diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp index d2fa7c0..737b7d2 100644 --- a/my_imgui_widgets.cpp +++ b/my_imgui_widgets.cpp @@ -219,6 +219,122 @@ 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); + + DebugWatchVar("ZoomSize: ", &TimelineZoomSize, d_float); + DebugWatchVar("MoveSize: ", &TimelineMoveSize, d_float); + DebugWatchVar("Percent Offset: ", &UI->TimelinePercentOffset, d_float); + DebugWatchVar("Percent Zoom: ", &UI->TimelinePercentZoomed, d_float); + + 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; + } + } + +#if 0 + x = -0.25; + bool32 LeftmostEdge = false; + while (!LeftmostEdge) { + ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineStartingPos.y); + ImVec2 Max = ImVec2(Min.x + 2, WindowMaxAbs.y); + if (Min.x > TimelineAbsolutePos.x) { + draw_list->AddLine(Min, Max, LineColor); + char buf2[6]; + sprintf(buf2, "%.2f", x); + draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); + x -= 0.25; + } else { + LeftmostEdge = true; + } + } +#endif +} + +static void +ImGui_TimelineIncrementDraw2(project_data *File, ui *UI, ImDrawList *draw_list, real32 MaxVal_Y, real32 MinVal_Y, + ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, bool32 IsText) +{ + uint32 LineColor = IM_COL32(200, 200, 200, 40); + + real32 TimelineZoomSize = TimelineSizeWithBorder.y / UI->Y_TimelinePercentZoomed; + real32 TimelineMoveSize = TimelineSizeWithBorder.y * UI->Y_TimelinePercentOffset / UI->Y_TimelinePercentZoomed; + + Assert(TimelineZoomSize > 0.0f); + + DebugWatchVar("Y_ZoomSize: ", &TimelineZoomSize, d_float); + DebugWatchVar("Y_MoveSize: ", &TimelineMoveSize, d_float); + DebugWatchVar("Y_Percent Offset: ", &UI->Y_TimelinePercentOffset, d_float); + DebugWatchVar("Y_Percent Zoom: ", &UI->Y_TimelinePercentZoomed, d_float); + + real32 x = 0; + bool32 RightmostEdge = false; + real32 Increment = 1 / MaxVal_Y; + + while (!RightmostEdge) { + ImVec2 Min = ImVec2(TimelineAbsolutePos.x, TimelineAbsolutePos.y + TimelineZoomSize + TimelineMoveSize - x*TimelineZoomSize); + ImVec2 Max = ImVec2(TimelineAbsolutePos.x + TimelineSizeWithBorder.x, Min.y + 2); + if (Min.y > TimelineAbsolutePos.y) { + draw_list->AddLine(Min, Max, LineColor); + char buf2[6]; + sprintf(buf2, "%.2f", x / Increment); + draw_list->AddText(ImVec2(TimelineAbsolutePos.x, Min.y), IM_COL32(200, 200, 200, 130), buf2); + x += Increment; + } else { + RightmostEdge = true; + } + } + +#if 0 + x = -0.25; + bool32 LeftmostEdge = false; + while (!LeftmostEdge) { + ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineStartingPos.y); + ImVec2 Max = ImVec2(Min.x + 2, WindowMaxAbs.y); + if (Min.x > TimelineAbsolutePos.x) { + draw_list->AddLine(Min, Max, LineColor); + char buf2[6]; + sprintf(buf2, "%.2f", x); + draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); + x -= 0.25; + } else { + LeftmostEdge = true; + } + } +#endif +} + +static void ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io) { if (State->MostRecentlySelectedLayer > -1) { @@ -973,15 +1089,19 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, 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) + + 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*4; + real32 TopbarHeight = FontHeight*2; ImVec2 TopbarMax = ImVec2(WindowMaxAbs.x, WindowMinAbs.y + TopbarHeight); ImVec2 TimelineBorderPadding = ImVec2(FontHeight, FontHeight); @@ -1010,14 +1130,10 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, 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 = {}; - + real32 MaxZoom = TimelineSizeWithBorder.x / (File->NumberOfFrames + 1); if (UI->Initializing) { - UI->TimelineZoom = TimelineSizeWithBorder.x / (File->NumberOfFrames + 1); + UI->TimelineZoom = MaxZoom; } ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -1031,7 +1147,9 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGui::BeginChild("Topbar", TopbarSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); - // ImGui::Text + char buf2[6]; + sprintf(buf2, "%.2i:%.2i", File->CurrentFrame / File->FPS, File->CurrentFrame % File->FPS); + ImGui::Text(buf2); /* ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); @@ -1040,6 +1158,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGui::SetCursorScreenPos(PlayheadPos); ImGui::Button("P", ButtonSize); + /* if (ImGui::IsItemActive()) { if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) { @@ -1059,6 +1178,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, } } } + */ ImGui::EndChild(); @@ -1072,105 +1192,6 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, 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::Button(BlendmodeNames[Layer->BlendMode]); - ImGui::OpenPopupOnItemClick("blendmode_picker", ImGuiPopupFlags_MouseButtonLeft); - if (ImGui::BeginPopup("blendmode_picker")) { - for (int16 b = 0; b < AmountOf(BlendmodeNames); b++) { - if (ImGui::MenuItem(BlendmodeNames[b], NULL, false, Layer->BlendMode != b)) { - Layer->BlendMode = (blend_mode)b; - State->UpdateFrame = true; - } - // using IsActivated here instead of above loop doesn't seem to - // work; the popup gets closed instead - if (ImGui::IsItemHovered() && io.KeyCtrl) { - Layer->BlendMode = (blend_mode)b; - State->UpdateFrame = true; - } - } - ImGui::EndPopup(); - } - 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")) - Keyframe_Insert(Property, Memory, File->CurrentFrame, Property->CurrentValue.f); - ImGui::SameLine(); - if (ImGui::Button("G")) { - UI->Graph[UI->NumberOfGraphsEnabled].ChannelViewed = Property; - UI->NumberOfGraphsEnabled++; - } - ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, YInit)); - ImGui::PopID(); - } - } - ImGui::PopStyleVar(); - - ImGui::PopID(); - } - ImGui::PopClipRect(); /// Split size adjuster @@ -1199,355 +1220,158 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGui::SetCursorScreenPos(TimelineStartingPos); - if (UI->NumberOfGraphsEnabled) - { - ImVec2 Dim = ImVec2(TimelineAbsolutePos.x + TimelineSizeWithBorder.x, TimelineAbsolutePos.y + TimelineSizeWithBorder.y - UI->Graph[0].WindowYOffset); - ImGui::PushClipRect(TimelineAbsolutePos, Dim, true); - draw_list->PushClipRect(TimelineAbsolutePos, Dim, true); - } else { - ImGui::PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); - draw_list->PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); - } + 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[0]; + property_channel *Property = &Layer->x; - 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); + real32 MaxVal_Y = 0; + 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; + } - // TODO(fox): Investigate why this button doesn't get lined up with - // leftbound in certain cases. (i.e. rotation property expanded with keyframes) + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { + ImGui::PushID(b); + real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; + real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; + real32 Y_TimelineZoomSize = TimelineSizeWithBorder.y / UI->Y_TimelinePercentZoomed; + real32 Y_TimelineMoveSize = TimelineSizeWithBorder.y * UI->Y_TimelinePercentOffset / UI->Y_TimelinePercentZoomed; - 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; - } + keyframe *Keyframe = KeyframeLookup(Property, b); + // Only used for drawing the bezier. + keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b) : NULL; - ImGui::Button("##rightbound", ImVec2(0.5 * UI->TimelineZoom, 0)); + real32 Ratio_X = (real32)(Layer->BitmapInfo.FrameOffset + Keyframe->FrameNumber) / File->NumberOfFrames; + real32 Ratio_Y = 1.0f - ((Keyframe->Value.f - MinVal_Y) / MaxVal_Y); - if (ImGui::IsItemHovered()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - } - ImGui_SlidingLayer(Layer, &UI->DraggingKeyframeThreshold, io.MouseDelta.x, UI->TimelineZoom, 2); + ImVec2 KeyframePos = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + Ratio_Y*Y_TimelineZoomSize); + ImGui::SetCursorScreenPos(KeyframePos); + ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); - for (int a = Layer->StartFrame; a < Layer->EndFrame; a++) { - cached_bitmap *Bitmap = Cache_CheckBitmap(Layer->Source, &Layer->BitmapInfo, Memory, a); - if (Bitmap) { - ImVec2 MinPos = ImVec2(TimelineStartingPos.x + UI->TimelineZoom * a, ImGui::GetCursorScreenPos().y); - draw_list->AddRect(MinPos, ImVec2(MinPos.x + UI->TimelineZoom, MinPos.y + 2.0f), ImColor(0, 255, 0, 255)); + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + // if ((ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_A))) + { + real32 MouseDeltaRatio = -io.MouseDelta.y / TimelineSizeWithBorder.y; + // real32 MouseDeltaRatio = 0.2; + Keyframe->Value.f += MouseDeltaRatio*MaxVal_Y*UI->TimelinePercentZoomed; + if ((Ratio_Y == 0) || Keyframe->Value.f > MaxVal_Y) { + UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed/(1+MouseDeltaRatio); + real32 Part1 = (1.0f - UI->Y_TimelinePercentOffset*MouseDeltaRatio)/(1+MouseDeltaRatio); + UI->Y_TimelinePercentOffset -= 1.0f - Part1; } } + ImGui::PopID(); + } - 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 = KeyframeLookup(Property, b); - real32 KeyframeOrigin = TimelineStartingPos.x + UI->TimelineZoom*Keyframe->FrameNumber; - ImVec2 KeyframePosition = ImVec2(KeyframeOrigin - FontHeight/2, InitialY); - - ImGui::PushID(Keyframe); - - ImGui::SetCursorScreenPos(KeyframePosition); + ImGui_TimelineIncrementDraw(File, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, 0); + ImGui_TimelineIncrementDraw2(File, UI, draw_list, MaxVal_Y, MinVal_Y, TimelineSizeWithBorder, TimelineAbsolutePos, 0); - // sadly ImGui::Selectable doesn't work here - if (Keyframe->IsSelected) - ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered)); +#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 - ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); - ImGui::SameLine(); + ImVec2 MouseDelta = io.MouseDelta / TimelineSize; - if (Keyframe->IsSelected) - ImGui::PopStyleColor(); + real32 BarHandleSize = FontHeight; + real32 BarThickness = 50; - 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; - } - } + real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset; + real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed); - ImGui_KeyframeDragging(File, State, UI, Property, b, io, 0); + // 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 = (BarH_Pos > 0) ? BarH_Pos : 0; + ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_Offset, TimelineSize.y - BarThickness); - ImGui::PopID(); - } + real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ? + TimelineSizeWithBorder.x - BarH_Pos : + BarH_Size + (BarH_Pos - BarH_Offset); - ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, NextY)); + if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x) + BarH_SizeUI = TimelineSizeWithBorder.x; - ImGui::PopID(); - } - } + BarH_SizeUI = BarH_SizeUI - BarHandleSize*2; - ImGui::SetCursorPosY(ImGui::GetCursorPos().y - (ItemSpacing.y * UI->KeyframeSpacing / 2)); - ImGui::PopStyleVar(); + ImGui::SetCursorScreenPos(BarH_PosUI); + ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); - ImGui::PopID(); + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed -= MouseDelta.x; + UI->TimelinePercentOffset -= MouseDelta.x; } + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0)); + ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness)); - // 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); - } + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelinePercentOffset -= MouseDelta.x; } + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0)); + ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness)); - draw_list->PopClipRect(); - ImGui::PopClipRect(); - - // Graph window - - if (UI->NumberOfGraphsEnabled) + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) { - ui_graph Graph = UI->Graph[0]; - - ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, - TimelineAbsolutePos.y + TimelineSizeWithBorder.y - Graph.WindowYOffset)); - - ImVec2 GraphMin = ImVec2(TimelineAbsolutePos.x, ImGui::GetCursorScreenPos().y); - ImVec2 GraphSize = ImVec2(TimelineSizeWithBorder.x, Graph.WindowYOffset); - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->AddRectFilled(GraphMin, GraphMin + GraphSize, - IM_COL32(0, 0, 30, 50)); - - // draw_list->PushClipRect(GraphMin, GraphMin + GraphSize, true); - // draw_list->PopClipRect(); - - /* - 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 = KeyframeLookup(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 = KeyframeLookup(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); + UI->TimelinePercentZoomed += MouseDelta.x; + } - 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 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2; + real32 BarV_Pos = -BarV_MaxSize * UI->Y_TimelinePercentOffset; + real32 BarV_Size = BarV_MaxSize / (1 / UI->Y_TimelinePercentZoomed); - 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 BarV_Offset = (BarV_Pos > 0) ? BarV_Pos : 0; + ImVec2 BarV_PosUI = TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, BarV_Offset); - real32 LocalHandlePosY = HandleYRatio * Property->GraphLength; - real32 LocalHandlePosY_L = HandleYRatio_L * Property->GraphLength; - real32 LocalHandlePosY_R = HandleYRatio_R * Property->GraphLength; + real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ? + BarV_MaxSize - BarV_Pos : + BarV_Size + (BarV_Pos - BarV_Offset); - 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; + if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize) + BarV_SizeUI = BarV_MaxSize; - ImVec2 HandlePos = ImVec2(HandlePosX, HandlePosY); - ImVec2 HandlePos_L = ImVec2(HandlePosX_L, HandlePosY_L); - ImVec2 HandlePos_R = ImVec2(HandlePosX_R, HandlePosY_R); + BarV_SizeUI = BarV_SizeUI - BarHandleSize*2; - MidPos[b & 1] = HandlePos; - LeftPos[b & 1] = HandlePos_L; - RightPos[b & 1] = HandlePos_R; + ImGui::SetCursorScreenPos(BarV_PosUI); + ImGui::Button("##h-scrollbarleft", ImVec2(BarThickness, BarHandleSize)); - 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 ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->Y_TimelinePercentZoomed -= MouseDelta.y; + UI->Y_TimelinePercentOffset -= MouseDelta.y; + } - 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); + ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize)); + ImGui::Button("##h-scrollbarhori", ImVec2(BarThickness, BarV_SizeUI)); - 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; - } - */ + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->Y_TimelinePercentOffset -= MouseDelta.y; } + 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; + } - // 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); + draw_list->PopClipRect(); + ImGui::PopClipRect(); ImGui::PopStyleVar(); @@ -1562,7 +1386,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left); bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right); - if (IsActive || AnimationCurves.IsItemActive) { + if (IsActive) { if (LeftClick) { if (io.KeyCtrl && IsActive) { real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x; @@ -1571,7 +1395,7 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, State->UpdateFrame = true; State->UpdateKeyframes = true; } else { - if (IsItemActivated || AnimationCurves.IsItemActivated) + if (IsItemActivated) { if (!io.KeyShift) { // DeselectAllKeyframes(&State); @@ -1597,11 +1421,10 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, if (ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1) ) { UI->TimelineZoom += io.MouseDelta.x; - ImGui::SetScrollX(ImGui::GetScrollMaxX() * UI->TempZoomRatioTimeline); } } } - if (IsItemDeactivated || AnimationCurves.IsItemDeactivated) { + if (IsItemDeactivated) { UI->BoxStart = {0, 0}; UI->BoxEnd = {0, 0}; UI->BoxSelectActive = false; @@ -1613,8 +1436,9 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGui::PopStyleVar(2); if (IsRectTouching(WindowMinAbs, WindowMaxAbs, io.MousePos, io.MousePos + 1)) { + real32 Multiplier = 16; if (io.KeyCtrl && io.MouseWheel) { - real32 ZoomAmount = io.MouseWheel*16; + real32 ZoomAmount = io.MouseWheel*Multiplier; real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x; real32 ZoomRatio = LocalMousePos / UI->TimelineZoom; if (UI->TimelineZoom + ZoomAmount > 0) { @@ -1622,10 +1446,10 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, UI->ScrollXOffset -= ZoomAmount*ZoomRatio; } } else if (io.KeyShift && io.MouseWheel) { - UI->ScrollXOffset += io.MouseWheel*16; + UI->ScrollXOffset += io.MouseWheel*Multiplier; } else { - UI->ScrollXOffset += io.MouseWheelH*16; - UI->ScrollYOffset += io.MouseWheel*16; + UI->ScrollXOffset += io.MouseWheelH*Multiplier; + UI->ScrollYOffset += io.MouseWheel*Multiplier; } } @@ -1638,8 +1462,12 @@ ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, static void ImGui_ProcessInputs(project_data *File, project_state *State, comp_buffer *CompBuffer, memory *Memory, ui *UI, ImGuiIO io) { - if (io.KeysData[ImGuiKey_Q].Down) + if (io.KeysData[ImGuiKey_Q].Down) { State->IsRunning = false; +#if DEBUG + // ImGui::SaveIniSettingsToDisk("asda"); +#endif + } if (ImGui::IsKeyPressed(ImGuiKey_D)) { IncrementFrame(File, -1); @@ -1893,3 +1721,611 @@ static char ImGuiPrefs[] = "[Window][DockSpaceViewport_11111111]" "\n DockNode ID=0x00000007 Parent=0x00000006 SizeRef=502,913 Selected=0x86FA2F90" "\n DockNode ID=0x00000008 Parent=0x00000006 SizeRef=502,256 Selected=0x812F222D" "\n DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=3200,627 HiddenTabBar=1 Selected=0x0F18B61B"; + + /* + // Graph window + + if (UI->NumberOfGraphsEnabled) + { + ui_graph Graph = UI->Graph[0]; + + ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, + TimelineAbsolutePos.y + TimelineSizeWithBorder.y - Graph.WindowYOffset)); + + ImVec2 GraphMin = ImVec2(TimelineAbsolutePos.x, ImGui::GetCursorScreenPos().y); + ImVec2 GraphSize = ImVec2(TimelineSizeWithBorder.x, Graph.WindowYOffset); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(GraphMin, GraphMin + GraphSize, + IM_COL32(0, 0, 30, 50)); + + // draw_list->PushClipRect(GraphMin, GraphMin + GraphSize, true); + // draw_list->PopClipRect(); + + 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 = KeyframeLookup(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 = KeyframeLookup(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; + } + } + */ + +#if 0 + // 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); + char buf2[6]; + if (((uint32)x % File->FPS) == 0) + sprintf(buf2, "%.2i:%.2i", (uint32)x / File->FPS, (uint32)x % File->FPS); + else + sprintf(buf2, ":%.2i", (uint32)x % File->FPS); + draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); + } + } +#else +#endif + +#if 0 + // 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); +#endif + +#if 0 + // Scrollbar + + real32 MaxScrollPos = UI->TimelineZoom * (File->NumberOfFrames + 1); + + real32 ScrollRatio = Normalize(-1 * (UI->ScrollXOffset / MaxScrollPos)); + DebugWatchVar("ScrollXOffset: %.2f", &UI->ScrollXOffset, d_float); + real32 BarRatio = (MaxZoom / UI->TimelineZoom); + DebugWatchVar("BarRatio: %.2f", &BarRatio, d_float); + + DebugWatchVar("Timelinezoom: %.2f", &UI->TimelineZoom, d_float); + + // ImVec2 SMinP = TimelineAbsolutePos + ImVec2(ScrollRatio * TimelineSizeWithBorder.x, TimelineSize.y - 200); + // ImVec2 SMaxP = TimelineAbsolutePos + ImVec2((ScrollRatio + BarRatio) * TimelineSizeWithBorder.x, TimelineSize.y + 200); + // draw_list->AddRectFilled(SMinP, SMaxP, + // IM_COL32(0, 200, 200, 50)); + ImVec2 BarMinPos = ImVec2(ScrollRatio * TimelineSizeWithBorder.x, TimelineSize.y - 50); + ImVec2 BarSize = ImVec2(BarRatio * TimelineSizeWithBorder.x, TimelineSize.y + 10); + + ImVec2 SideSize = ImVec2(FontHeight, 0); + + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarMinPos); + ImGui::Button("##scrollbarleft", ImVec2(SideSize.x, BarSize.y)); + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarMinPos + SideSize); + ImGui::Button("##scrollbarhori", BarSize - SideSize*2); + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + UI->ScrollXOffset -= io.MouseDelta.x / BarRatio; + } + + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarMinPos + ImVec2(BarSize.x, 0) - SideSize); + ImGui::Button("##scrollbarright", ImVec2(SideSize.x, BarSize.y)); + + if (ImGui::IsItemActivated()) { + UI->TempZoomRatioTimeline = UI->TimelineZoom; + } + + bool32 asda = false; + if (ImGui::IsKeyPressed(ImGuiKey_F)) { + asda = true; + } + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) || asda) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + + real32 test = asda ? 0.125 : (-io.MouseDelta.x / TimelineSize.x); + real32 MouseDelta = TimelineSize.x * test; + real32 ZoomAmount = MouseDelta / (TimelineSize.x * (1.0f - test)) * UI->TimelineZoom; + UI->TimelineZoom += ZoomAmount/BarRatio; + // if (asda) + // UI->ScrollXOffset -= MouseDelta / BarRatio; + } + + ImGui::SameLine(); + +#endif + + +#if 0 + 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::Button(BlendmodeNames[Layer->BlendMode]); + ImGui::OpenPopupOnItemClick("blendmode_picker", ImGuiPopupFlags_MouseButtonLeft); + if (ImGui::BeginPopup("blendmode_picker")) { + for (int16 b = 0; b < AmountOf(BlendmodeNames); b++) { + if (ImGui::MenuItem(BlendmodeNames[b], NULL, false, Layer->BlendMode != b)) { + Layer->BlendMode = (blend_mode)b; + State->UpdateFrame = true; + } + // using IsActivated here instead of above loop doesn't seem to + // work; the popup gets closed instead + if (ImGui::IsItemHovered() && io.KeyCtrl) { + Layer->BlendMode = (blend_mode)b; + State->UpdateFrame = true; + } + } + ImGui::EndPopup(); + } + 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")) + Keyframe_Insert(Property, Memory, File->CurrentFrame, Property->CurrentValue.f); + ImGui::SameLine(); + if (ImGui::Button("G")) { + UI->Graph[UI->NumberOfGraphsEnabled].ChannelViewed = Property; + UI->NumberOfGraphsEnabled++; + } + ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, YInit)); + ImGui::PopID(); + } + } + ImGui::PopStyleVar(); + + ImGui::PopID(); + } +#endif + + + /* + 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); + + // TODO(fox): Investigate why this button doesn't get lined up with + // leftbound in certain cases. (i.e. rotation property expanded with keyframes) + + 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); + + for (int a = Layer->StartFrame; a < Layer->EndFrame; a++) { + cached_bitmap *Bitmap = Cache_CheckBitmap(Layer->Source, &Layer->BitmapInfo, Memory, a); + if (Bitmap) { + ImVec2 MinPos = ImVec2(TimelineStartingPos.x + UI->TimelineZoom * a, ImGui::GetCursorScreenPos().y); + draw_list->AddRect(MinPos, ImVec2(MinPos.x + UI->TimelineZoom, MinPos.y + 2.0f), ImColor(0, 255, 0, 255)); + } + } + + 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 = KeyframeLookup(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)); + + ImGui::PopID(); + } + } + + ImGui::SetCursorPosY(ImGui::GetCursorPos().y - (ItemSpacing.y * UI->KeyframeSpacing / 2)); + ImGui::PopStyleVar(); + + ImGui::PopID(); + } + */ + + +#if 0 + +// Old code: + + ImVec2 SideSize = ImVec2(FontHeight, 0); + real32 BarYSize = 50; + real32 BarYPos = TimelineSize.y - BarYSize; + + real32 BarXPos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset; + real32 BarXSize = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed); + + real32 MouseDelta = io.MouseDelta.x / TimelineSize.x; + + // A bit of manipulation to clip the bar handles to the edges if they go over. + real32 LeftPos = (BarXPos > 0) ? BarXPos : 0; + ImVec2 BarClippedLeftPos(LeftPos, BarYPos); + + real32 BarOffset = BarXPos - LeftPos; + real32 BarClippedSize = (BarXSize + BarXPos > TimelineSizeWithBorder.x) ? TimelineSizeWithBorder.x - BarXPos : BarXSize + BarOffset; + if (LeftPos == 0 && BarClippedSize > TimelineSizeWithBorder.x) BarClippedSize = TimelineSizeWithBorder.x; + + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarClippedLeftPos); + ImGui::Button("##scrollbarleft", ImVec2(SideSize.x, BarYSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed -= MouseDelta; + UI->TimelinePercentOffset -= MouseDelta; + } + + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarClippedLeftPos + SideSize); + ImGui::Button("##scrollbarhori", ImVec2(BarClippedSize, BarYSize) - SideSize*2); + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelinePercentOffset -= MouseDelta; + } + + ImGui::SetCursorScreenPos(TimelineAbsolutePos + BarClippedLeftPos + ImVec2(BarClippedSize, 0) - SideSize ); + ImGui::Button("##scrollbarright", ImVec2(SideSize.x, BarYSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed += MouseDelta; + } + +// New code: + + ImVec2 MouseDelta = io.MouseDelta / TimelineSize; + + real32 BarHandleSize = FontHeight; + real32 BarThickness = 50; + + real32 BarH_XPos = -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_XOffset = (BarH_XPos > 0) ? BarH_XPos : 0; + ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_XOffset, TimelineSize.y - BarThickness); + + real32 BarH_SizeUI = (BarH_Size + BarH_XPos > TimelineSizeWithBorder.x) ? + TimelineSizeWithBorder.x - BarH_XPos : + BarH_Size + (BarH_XPos - BarH_XOffset); + + if (BarH_XOffset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x) + BarH_SizeUI = TimelineSizeWithBorder.x; + + BarH_SizeUI = BarH_SizeUI - BarHandleSize*2; + + ImGui::SetCursorScreenPos(BarH_PosUI); + ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed -= MouseDelta.x; + UI->TimelinePercentOffset -= MouseDelta.x; + } + + 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; + } + + 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))) + { + UI->TimelinePercentZoomed += MouseDelta.x; + } + +#endif @@ -443,6 +443,18 @@ struct rectangle v2i Max; }; +inline real32 +Min(real32 A, real32 B) +{ + return (A > B) ? B : A; +} + +inline real32 +Max(real32 A, real32 B) +{ + return (A > B) ? A : B; +} + inline bool32 TestRectangle(rectangle Rect, v2i Test) { |