summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFox Caminiti <fox@foxcam.net>2022-09-29 20:58:54 -0400
committerFox Caminiti <fox@foxcam.net>2022-09-29 20:58:54 -0400
commit3b8bd135662d99506e8a2ebb30b0d46b57861f74 (patch)
tree533884d6ada9a14e711fa70f2e4526f247cd6034
parent02d7df95cfc2402f0488f66f1dc5fa84cae00934 (diff)
rewritten graph ui start
-rw-r--r--.gitignore1
-rw-r--r--createcalls.cpp4
-rw-r--r--main.cpp13
-rw-r--r--main.h13
-rw-r--r--my_imgui_widgets.cpp1286
-rw-r--r--my_math.h12
6 files changed, 898 insertions, 431 deletions
diff --git a/.gitignore b/.gitignore
index 4edc6f9..f3aa2ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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;
diff --git a/main.cpp b/main.cpp
index 866ef24..5d12d91 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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
diff --git a/main.h b/main.h
index 967354f..42befc8 100644
--- a/main.h
+++ b/main.h
@@ -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
diff --git a/my_math.h b/my_math.h
index e1144e3..df5f8a6 100644
--- a/my_math.h
+++ b/my_math.h
@@ -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)
{