Created
March 28, 2018 02:41
-
-
Save JSandusky/92ed08029bd79b9256b2a0ce1c380074 to your computer and use it in GitHub Desktop.
ImSequencer Revisions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ImSequencer.h" | |
#include "imgui.h" | |
#include "imgui_internal.h" | |
#include <vector> | |
namespace ImSequencer | |
{ | |
struct ScrollBarInfo | |
{ | |
bool inTrack = false; | |
bool inThumb = false; | |
bool IsActive() { return inThumb || inTrack; } | |
void Deactivate() { inThumb = inTrack = false; } | |
}; | |
struct Context | |
{ | |
bool inTrackHeader = false; | |
ScrollBarInfo horizontalScroll; | |
ScrollBarInfo verticalScroll; | |
int mouseFrameIndex = -1; | |
int movingTrack = -1; | |
int movingKey = -1; | |
int movingPos = -1; | |
int movingPart = -1; | |
int verticalOffset = 0; | |
}; | |
static Context gContext; | |
static bool SequencerAddDelButton(ImDrawList* draw_list, ImVec2 pos, bool add = true) | |
{ | |
ImGuiIO& io = ImGui::GetIO(); | |
ImRect delRect(pos, ImVec2(pos.x + 16, pos.y + 16)); | |
bool overDel = delRect.Contains(io.MousePos); | |
int delColor = overDel ? 0xFFAAAAAA : 0x50AAAAAA; | |
//int delColor = overDel ? 0xFFAAAAAA : 0x50000000; | |
float midy = pos.y + 16 / 2 - 0.5f; | |
float midx = pos.x + 16 / 2 - 0.5f; | |
draw_list->AddRect(delRect.Min, delRect.Max, delColor, 4); | |
draw_list->AddLine(ImVec2(delRect.Min.x + 3, midy), ImVec2(delRect.Max.x - 3, midy), delColor, 2); | |
if (add) | |
draw_list->AddLine(ImVec2(midx, delRect.Min.y + 3), ImVec2(midx, delRect.Max.y - 3), delColor, 2); | |
return overDel; | |
} | |
static int min(int a, int b) { return (a < b) ? a : b; } | |
static int max(int a, int b) { return (a > b) ? a : b; } | |
bool Sequencer(SequenceInterface *sequence, int *currentFrame, bool *expanded, int *selectedEntry, int* selectedKey, int *firstFrame, int sequenceOptions) | |
{ | |
bool ret = false; | |
ImGuiIO& io = ImGui::GetIO(); | |
int cx = (int)(io.MousePos.x); | |
int cy = (int)(io.MousePos.y); | |
int framePixelWidth = 16; | |
int legendWidth = 300; | |
const float scrollX = ImGui::GetScrollX(); | |
const float scrollY = ImGui::GetScrollY(); | |
int delEntry = -1; | |
int dupEntry = -1; | |
int ItemHeight = ImGui::GetFont()->FontSize; | |
bool popupOpened = false; | |
ImGui::BeginGroup(); | |
ImDrawList* draw_list = ImGui::GetWindowDrawList(); | |
ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! | |
ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available | |
static const int scrollBarHeight = 24; | |
int firstFrameUsed = firstFrame ? *firstFrame : 0; | |
int sequenceCount = sequence->GetTrackCount(); | |
int controlHeight = (sequenceCount + 1) * ItemHeight; | |
int frameCount = sequence->GetFrameCount(); | |
if (expanded && !*expanded) | |
{ | |
ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x - canvas_pos.x, (float)ItemHeight)); | |
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0); | |
char tmps[512]; | |
int keyCt = 0; | |
for (int i = 0; i < sequenceCount; ++i) | |
keyCt += sequence->GetKeyFrameCount(i); | |
sprintf_s(tmps, sizeof(tmps), "%d Frames / %d tracks / %d keyframes", frameCount, sequenceCount, keyCt); | |
draw_list->AddText(ImVec2(canvas_pos.x + 26, canvas_pos.y + 2), 0xFFFFFFFF, tmps); | |
} | |
else | |
{ | |
bool hasHorizScrollBar = false; | |
bool hasVerticalScrollBar = false; | |
int framesPixelWidth = frameCount * framePixelWidth; | |
if ((framesPixelWidth + legendWidth) >= canvas_size.x) | |
{ | |
hasHorizScrollBar = true; | |
controlHeight += scrollBarHeight; | |
} | |
if (controlHeight > canvas_size.y) | |
hasVerticalScrollBar = true; | |
const int visibleTrackCount = hasVerticalScrollBar ? floorf((canvas_size.y - (hasHorizScrollBar ? scrollBarHeight : 0) - ItemHeight) / ItemHeight) : sequenceCount; | |
const int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth); | |
const float effectiveHeight = (visibleTrackCount + 1) * ItemHeight; | |
ImRect backgroundRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + effectiveHeight)); | |
ImGui::InvisibleButton("canvas", ImVec2(canvas_size.x, effectiveHeight)); | |
// full background | |
draw_list->AddRectFilled(backgroundRect.Min, backgroundRect.Max, 0xFF262222, 0); | |
// current frame top | |
ImRect topRect(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y + scrollY), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight + scrollY)); | |
// Move the current time caret line | |
if (sequenceOptions & SEQUENCER_CHANGE_FRAME && currentFrame && *currentFrame >= 0 && (topRect.Contains(io.MousePos) || gContext.inTrackHeader) && io.MouseDown[0] && gContext.movingTrack == -1) | |
{ | |
ImGui::CaptureMouseFromApp(); | |
*currentFrame = (int)(io.MousePos.x - topRect.Min.x) / framePixelWidth + firstFrameUsed; | |
if (*currentFrame < 0) | |
*currentFrame = 0; | |
if (*currentFrame >= frameCount) | |
*currentFrame = frameCount - 1; | |
if (firstFrame && *currentFrame < *firstFrame) | |
*firstFrame = *currentFrame; | |
if (firstFrame && *currentFrame > *firstFrame + visibleFrameCount - 1) | |
*firstFrame = *currentFrame - visibleFrameCount; | |
// have to do this again?? | |
if (*currentFrame >= frameCount) | |
*currentFrame = frameCount - 1; | |
gContext.inTrackHeader = true; | |
// Text cursor resembles the time position | |
ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); | |
} | |
else if (!io.MouseDown[0] && gContext.inTrackHeader) | |
gContext.inTrackHeader = false; | |
// Setup the mouse frame | |
gContext.mouseFrameIndex = (int)(io.MousePos.x - (canvas_pos.x + legendWidth)) / framePixelWidth + firstFrameUsed; | |
if (gContext.mouseFrameIndex < 0) | |
gContext.mouseFrameIndex = 0; | |
if (gContext.mouseFrameIndex >= frameCount) | |
gContext.mouseFrameIndex = frameCount - 1; | |
//header | |
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0); | |
if (sequenceOptions&SEQUENCER_ADD) | |
{ | |
if (SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight, canvas_pos.y + 2), true) && io.MouseReleased[0]) | |
ImGui::OpenPopup("addEntry"); | |
if (ImGui::BeginPopup("addEntry")) | |
{ | |
for (int i = 0; i < sequence->GetTrackTypeCount(); i++) | |
if (ImGui::Selectable(sequence->GetTrackTypeName(i))) | |
{ | |
sequence->Add(i); | |
*selectedEntry = sequence->GetTrackCount() - 1; | |
*selectedKey = -1; | |
} | |
ImGui::EndPopup(); | |
popupOpened = true; | |
} | |
} | |
float curY = canvas_pos.y + ItemHeight; | |
for (int i = gContext.verticalOffset; i < sequenceCount && i < gContext.verticalOffset + visibleTrackCount; i++) | |
{ | |
int type; | |
sequence->Get(i, -1, NULL, NULL, &type, NULL); | |
ImVec2 tpos(canvas_pos.x + 3, curY + 2); | |
draw_list->AddText(tpos, 0xFFFFFFFF, sequence->GetTrackLabel(i)); | |
curY += ItemHeight; | |
if (sequenceOptions&SEQUENCER_DEL) | |
{ | |
bool overDel = SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight + 2 - 10, tpos.y + 2), false); | |
if (overDel && io.MouseReleased[0]) | |
delEntry = i; | |
bool overDup = SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + legendWidth - ItemHeight - ItemHeight + 2 - 10, tpos.y + 2), true); | |
if (overDup && io.MouseReleased[0]) | |
dupEntry = i; | |
} | |
} | |
// clipping rect so items bars are not visible in the legend on the left when scrolled | |
ImRect outerClip(ImVec2(canvas_pos.x + legendWidth, canvas_pos.y + scrollY), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + controlHeight + scrollY - ImGui::GetStyle().FramePadding.y)); | |
draw_list->PushClipRect(outerClip.Min, outerClip.Max, true); | |
ImRect innerClip = outerClip; | |
innerClip.Max.x -= hasVerticalScrollBar ? (float)framePixelWidth : 0; | |
innerClip.Max.y -= hasHorizScrollBar ? (float)scrollBarHeight : 0; | |
// slots background | |
curY = canvas_pos.y + ItemHeight; | |
for (int i = gContext.verticalOffset; i < sequenceCount && i < gContext.verticalOffset + visibleTrackCount; i++) | |
{ | |
unsigned int col = (i & 1) ? 0xFF3A3636 : 0xFF413D3D; | |
ImVec2 pos = ImVec2(canvas_pos.x + legendWidth, curY + 1); | |
curY += ItemHeight; | |
ImVec2 sz = ImVec2(canvas_size.x + canvas_pos.x, pos.y + ItemHeight - 1); | |
if (!popupOpened && cy >= pos.y && cy < pos.y + ItemHeight && gContext.movingTrack == -1 && cx>canvas_pos.x && cx < canvas_pos.x + canvas_size.x) | |
{ | |
col += 0x80201008; | |
pos.x -= legendWidth; | |
} | |
draw_list->AddRectFilled(pos, sz, col, 0); | |
} | |
// Timeline ticks and counts | |
draw_list->AddRectFilled(canvas_pos, ImVec2(canvas_size.x + canvas_pos.x, canvas_pos.y + ItemHeight), 0xFF3D3837, 0); | |
for (int i = 0; i <= frameCount; i++) | |
{ | |
bool baseIndex = ((i % 10) == 0) || (i == frameCount); | |
bool halfIndex = (i % 5) == 0; | |
int px = (int)canvas_pos.x + i * framePixelWidth + legendWidth - firstFrameUsed * framePixelWidth; | |
int tiretStart = baseIndex ? 4 : (halfIndex ? 10 : 14); | |
int tiretEnd = baseIndex ? effectiveHeight : ItemHeight; | |
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)tiretStart), ImVec2((float)px, canvas_pos.y + (float)tiretEnd - 1), 0xFF606060, 1); | |
draw_list->AddLine(ImVec2((float)px, canvas_pos.y + (float)ItemHeight), ImVec2((float)px, canvas_pos.y + (visibleTrackCount+1) * ItemHeight - 1), 0x30606060, 1); | |
if (baseIndex) | |
{ | |
char tmps[512]; | |
sprintf_s(tmps, sizeof(tmps), "%d", i);// (i == frameCount) ? i : (i / 10)); | |
draw_list->AddText(ImVec2((float)px + 3.f, canvas_pos.y), 0xFFBBBBBB, tmps); | |
} | |
} | |
draw_list->AddLine(canvas_pos, ImVec2(canvas_pos.x, canvas_pos.y + canvas_size.y), 0xFF000000, 1); | |
draw_list->AddLine(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight), 0xFF000000, 1); | |
// selection | |
bool selected = selectedEntry && (*selectedEntry >= 0); | |
if (selected) | |
{ | |
draw_list->AddRectFilled(ImVec2(canvas_pos.x, canvas_pos.y + ItemHeight * (*selectedEntry + 1)), ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + ItemHeight * (*selectedEntry + 2)), 0x301080FF, 1.f); | |
} | |
// slots | |
curY = canvas_pos.y + ItemHeight; | |
ImGui::PushClipRect(innerClip.Min, innerClip.Max, true); | |
for (int i = gContext.verticalOffset; i < sequenceCount && i < gContext.verticalOffset + visibleTrackCount; i++) | |
{ | |
TRACK_NATURE nature = sequence->GetTrackNature(i); | |
unsigned keyCt = sequence->GetKeyFrameCount(i); | |
for (int keyIndex = 0; keyIndex < keyCt; ++keyIndex) | |
{ | |
int *start, *end; | |
unsigned int color; | |
sequence->Get(i, keyIndex, &start, &end, NULL, &color); | |
ImVec2 pos = ImVec2(canvas_pos.x + legendWidth - firstFrameUsed * framePixelWidth, curY + 1); | |
ImVec2 slotP1(pos.x + *start * framePixelWidth, pos.y + 2); | |
ImVec2 slotP2(pos.x + *end * framePixelWidth + framePixelWidth, pos.y + ItemHeight - 2); | |
// when a tick always force to 1 keyframe wide | |
if (nature == TRACK_NATURE_TICK) | |
slotP2.x = slotP1.x + framePixelWidth; | |
unsigned int slotColor = color | 0xFF000000; | |
const unsigned int quadColor[] = { 0xFFFFFFFF, 0xFFFFFFFF, (ImU32)(ImColor(slotColor) * 1.5f) | 0xFF000000 }; | |
const bool drawSelected = selectedEntry && *selectedEntry == i && selectedKey && *selectedKey == keyIndex; | |
if (nature == TRACK_NATURE_TICK) | |
{ | |
ImVec2 pts[4] = { | |
ImVec2(slotP1.x, slotP1.y + (slotP2.y - slotP1.y) * 0.5f), | |
ImVec2(slotP1.x + (slotP2.x - slotP1.x) * 0.5f, slotP1.y), | |
ImVec2(slotP2.x, slotP1.y + (slotP2.y - slotP1.y) * 0.5f), | |
ImVec2(slotP1.x + (slotP2.x - slotP1.x) * 0.5f, slotP2.y), | |
}; | |
draw_list->AddConvexPolyFilled(pts, 4, drawSelected ? quadColor[2] : slotColor); | |
draw_list->AddPolyline(pts, 4, drawSelected ? quadColor[0] : ImColor(slotColor) * 0.5f, true, 1.0f); | |
} | |
else | |
draw_list->AddRectFilled(slotP1, slotP2, drawSelected ? quadColor[2] : slotColor, 2); | |
ImRect mainRect = ImRect(slotP1, slotP2); | |
ImRect rects[3] = { ImRect(slotP1, ImVec2(slotP1.x + framePixelWidth / 2, slotP2.y)) | |
, ImRect(ImVec2(slotP2.x - framePixelWidth / 2, slotP1.y), slotP2) | |
, ImRect(slotP1, slotP2) }; | |
if (innerClip.Contains(io.MousePos) && gContext.movingTrack == -1 && (sequenceOptions & SEQUENCER_EDIT_STARTEND) && !gContext.inTrackHeader && !gContext.horizontalScroll.IsActive()) | |
{ | |
if (nature == TRACK_NATURE_TICK) | |
{ | |
if (mainRect.Contains(io.MousePos)) | |
{ | |
ImVec2 pts[4] = { | |
ImVec2(slotP1.x, slotP1.y + (slotP2.y - slotP1.y) * 0.5f), | |
ImVec2(slotP1.x + (slotP2.x - slotP1.x) * 0.5f, slotP1.y), | |
ImVec2(slotP2.x, slotP1.y + (slotP2.y - slotP1.y) * 0.5f), | |
ImVec2(slotP1.x + (slotP2.x - slotP1.x) * 0.5f, slotP2.y), | |
}; | |
draw_list->AddConvexPolyFilled(pts, 4, quadColor[2]); | |
draw_list->AddPolyline(pts, 4, quadColor[0], true, 1.0f); | |
if (io.MouseDown[0]) | |
{ | |
gContext.movingTrack = i; | |
gContext.movingKey = keyIndex; | |
gContext.movingPos = cx; | |
gContext.movingPart = 3; | |
break; | |
} | |
} | |
} | |
else | |
{ | |
for (int j = 2; j >= 0; j--) | |
{ | |
ImRect& rc = rects[j]; | |
if (!rc.Contains(io.MousePos)) | |
continue; | |
draw_list->AddRectFilled(rc.Min, rc.Max, quadColor[j], 2); | |
} | |
for (int j = 0; j < 3; j++) | |
{ | |
ImRect& rc = rects[j]; | |
if (!rc.Contains(io.MousePos)) | |
continue; | |
if (io.MouseDown[0]) | |
{ | |
gContext.movingTrack = i; | |
gContext.movingKey = keyIndex; | |
gContext.movingPos = cx; | |
gContext.movingPart = j + 1; | |
break; | |
} | |
} | |
} | |
} | |
} | |
curY += ItemHeight; | |
} | |
ImGui::PopClipRect(); | |
// moving | |
if (innerClip.Contains(io.MousePos) && gContext.movingTrack >= 0) | |
{ | |
TRACK_NATURE nature = sequence->GetTrackNature(gContext.movingTrack); | |
ImGui::CaptureMouseFromApp(); | |
// need to grab key starts for reordering keys | |
unsigned keyCt = sequence->GetKeyFrameCount(gContext.movingTrack); | |
std::vector<int> keyStarts; | |
for (int keyIndex = 0; keyIndex < keyCt; ++keyIndex) | |
{ | |
int *start; | |
sequence->Get(gContext.movingTrack, keyIndex, &start, 0x0, 0x0, 0x0); | |
keyStarts.push_back(*start); | |
} | |
int diffFrame = (cx - gContext.movingPos) / framePixelWidth; | |
if (abs(diffFrame) > 0) | |
{ | |
int *start, *end; | |
sequence->Get(gContext.movingTrack, gContext.movingKey, &start, &end, NULL, NULL); | |
int & l = *start; | |
int & r = *end; | |
if (nature == TRACK_NATURE_TICK) | |
{ | |
l += diffFrame; | |
r += diffFrame; | |
l = max(min(l, frameCount), 0); | |
r = l + 1; | |
} | |
else | |
{ | |
if (gContext.movingPart & 1) | |
l += diffFrame; | |
if (gContext.movingPart & 2) | |
r += diffFrame; | |
if (l < 0) | |
{ | |
if (gContext.movingPart & 2) | |
r -= l; | |
l = 0; | |
} | |
if (gContext.movingPart & 1 && l > r) | |
l = r; | |
if (gContext.movingPart & 2 && r < l) | |
r = l; | |
} | |
// swap key orders | |
if (gContext.movingKey > 0) | |
{ | |
if (keyStarts[gContext.movingKey - 1] > l) | |
{ | |
if (sequence->SwapKeyframes(gContext.movingTrack, gContext.movingKey, gContext.movingKey - 1)) | |
gContext.movingKey = gContext.movingKey - 1; | |
} | |
} | |
if (gContext.movingKey < keyCt - 1) | |
{ | |
if (keyStarts[gContext.movingKey + 1] < l) | |
{ | |
if (sequence->SwapKeyframes(gContext.movingTrack, gContext.movingKey, gContext.movingKey + 1)) | |
gContext.movingKey = gContext.movingKey + 1; | |
} | |
} | |
while (l < *firstFrame) | |
*firstFrame -= 1; | |
while (l > *firstFrame + visibleFrameCount || r > *firstFrame + visibleFrameCount) | |
*firstFrame += 1; | |
gContext.movingPos += diffFrame * framePixelWidth; | |
} | |
if (!io.MouseDown[0]) | |
{ | |
// single select | |
if (!diffFrame && gContext.movingPart && selectedEntry) | |
{ | |
*selectedEntry = gContext.movingTrack; | |
*selectedKey = gContext.movingKey; | |
ret = true; | |
} | |
gContext.movingTrack = -1; | |
} | |
else if (gContext.movingTrack != -1) | |
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); | |
} | |
// cursor | |
if (currentFrame && *currentFrame >= 0) | |
{ | |
float cursorOffset = canvas_pos.x + legendWidth + *currentFrame * framePixelWidth + framePixelWidth / 2 - (firstFrameUsed * framePixelWidth); | |
draw_list->AddLine(ImVec2(cursorOffset, canvas_pos.y), ImVec2(cursorOffset, canvas_pos.y + effectiveHeight), 0x902A2AFF, 4); | |
} | |
draw_list->PopClipRect(); | |
// copy paste | |
if (sequenceOptions & SEQUENCER_COPYPASTE) | |
{ | |
ImRect rectCopy(ImVec2(canvas_pos.x + 100, canvas_pos.y + 2) | |
, ImVec2(canvas_pos.x + 100 + 30, canvas_pos.y + ItemHeight - 2)); | |
bool inRectCopy = rectCopy.Contains(io.MousePos); | |
unsigned int copyColor = inRectCopy ? 0xFF1080FF : 0xFF000000; | |
draw_list->AddText(rectCopy.Min, copyColor, "Copy"); | |
ImRect rectPaste(ImVec2(canvas_pos.x + 140, canvas_pos.y + 2) | |
, ImVec2(canvas_pos.x + 140 + 30, canvas_pos.y + ItemHeight - 2)); | |
bool inRectPaste = rectPaste.Contains(io.MousePos); | |
unsigned int pasteColor = inRectPaste ? 0xFF1080FF : 0xFF000000; | |
draw_list->AddText(rectPaste.Min, pasteColor, "Paste"); | |
if (inRectCopy && io.MouseReleased[0]) | |
{ | |
sequence->Copy(); | |
} | |
if (inRectPaste && io.MouseReleased[0]) | |
{ | |
sequence->Paste(); | |
} | |
} | |
if (hasHorizScrollBar) | |
{ | |
const float vertTake = hasVerticalScrollBar ? framePixelWidth*2 : framePixelWidth; | |
int scrollBarStartHeight = canvas_size.y - scrollBarHeight; | |
// ratio = number of frames visible in control / number to total frames | |
int visibleFrameCount = (int)floorf((canvas_size.x - legendWidth) / framePixelWidth); | |
float barWidthRatio = visibleFrameCount / (float)frameCount; | |
float barWidthInPixels = barWidthRatio * (canvas_size.x - legendWidth - vertTake); | |
const float xBase = canvas_pos.x + legendWidth; | |
float startFrameOffset = ((float)firstFrameUsed / (float)frameCount) * (canvas_size.x - legendWidth); | |
ImVec2 scrollBarA(xBase, canvas_pos.y + scrollBarStartHeight); | |
ImVec2 scrollBarB(xBase + canvas_size.x - vertTake - legendWidth, canvas_pos.y + canvas_size.y); | |
draw_list->AddRectFilled(scrollBarA, scrollBarB, 0xFF222222, 0); | |
ImRect scrollBarRect(scrollBarA, scrollBarB); | |
bool inScrollBar = scrollBarRect.Contains(io.MousePos); | |
ImVec2 scrollBarC(xBase + startFrameOffset, canvas_pos.y + scrollBarStartHeight + 2); | |
ImVec2 scrollBarD(xBase + barWidthInPixels + startFrameOffset, canvas_pos.y + canvas_size.y - 2); | |
bool inThumb = ImRect(scrollBarC, scrollBarD).Contains(io.MousePos); | |
bool processScrollBar = (inThumb | inScrollBar | gContext.horizontalScroll.IsActive()) && gContext.movingTrack == -1; | |
if (processScrollBar && io.MouseDown[0] && firstFrame) | |
{ | |
if (inThumb || gContext.horizontalScroll.inThumb) | |
{ | |
ImGui::CaptureMouseFromApp(); | |
auto delta = ImGui::GetMouseDragDelta(0); | |
float ff = *firstFrame; | |
ff += delta.x / framePixelWidth; | |
int newFrame = roundf(ff); | |
if (newFrame != *firstFrame) | |
{ | |
ImGui::ResetMouseDragDelta(); | |
*firstFrame = newFrame; | |
} | |
gContext.horizontalScroll.inThumb = true; | |
} | |
else | |
*firstFrame = (int)(frameCount * ((io.MousePos.x - canvas_pos.x - legendWidth) / (canvas_size.x - legendWidth))); | |
*firstFrame = max(min(*firstFrame, frameCount - visibleFrameCount), 0); | |
gContext.horizontalScroll.inTrack = true; | |
ImGui::GetCurrentContext()->ActiveId = 1; | |
} | |
else if (!io.MouseDown[0] && gContext.horizontalScroll.IsActive()) | |
{ | |
gContext.horizontalScroll.Deactivate(); | |
} | |
draw_list->AddRectFilled(scrollBarC, scrollBarD, inScrollBar ? 0xFF606060 : 0xFF505050, 2); | |
} | |
else if (firstFrame) | |
{ | |
*firstFrame = 0; | |
gContext.horizontalScroll.Deactivate(); | |
} | |
else | |
{ | |
gContext.horizontalScroll.Deactivate(); | |
} | |
if (hasVerticalScrollBar) | |
{ | |
float scrollBarStartHeight = canvas_pos.y + ItemHeight; | |
float scrollBarRight = canvas_pos.x + canvas_size.x; | |
float scrollBarLeft = scrollBarRight - framePixelWidth; | |
float scrollBarBottom = canvas_pos.y + canvas_size.y - (hasHorizScrollBar ? scrollBarHeight : 0); | |
ImRect scrollRect({ scrollBarLeft, scrollBarStartHeight }, { scrollBarRight, scrollBarBottom }); | |
draw_list->AddRectFilled(scrollRect.Min, scrollRect.Max, 0xFF222222, 0); | |
bool inTrack = scrollRect.Contains(io.MousePos); | |
float scrollTop = gContext.verticalOffset * ItemHeight + 2; | |
float contentSize = effectiveHeight / controlHeight; | |
scrollTop = scrollTop * contentSize; | |
contentSize = effectiveHeight * contentSize; | |
ImRect thumbRect({ scrollBarLeft, scrollBarStartHeight + scrollTop }, { scrollBarRight, scrollBarStartHeight + contentSize + scrollTop - 4 }); | |
bool inThumb = thumbRect.Contains(io.MousePos); | |
bool inScrollBar = (inThumb || inTrack || gContext.verticalScroll.IsActive()) && gContext.movingTrack == -1; | |
if (inScrollBar && io.MouseDown[0]) | |
{ | |
if (inThumb || gContext.verticalScroll.inThumb) | |
{ | |
ImGui::CaptureMouseFromApp(); | |
auto delta = ImGui::GetMouseDragDelta(0); | |
float ff = gContext.verticalOffset; | |
ff += delta.y / framePixelWidth; | |
int newOffset = roundf(ff); | |
if (newOffset != gContext.verticalOffset) | |
{ | |
ImGui::ResetMouseDragDelta(); | |
gContext.verticalOffset = newOffset; | |
} | |
gContext.verticalScroll.inThumb = true; | |
} | |
else | |
gContext.verticalOffset = (int)(sequenceCount * ((io.MousePos.y - canvas_pos.y - ItemHeight) / (canvas_size.y - ItemHeight))); | |
gContext.verticalScroll.inTrack = true; | |
ImGui::GetCurrentContext()->ActiveId = 1; | |
} | |
else if (!io.MouseDown[0]) | |
gContext.verticalScroll.Deactivate(); | |
gContext.verticalOffset = max(min(gContext.verticalOffset, sequenceCount - visibleTrackCount), 0); | |
draw_list->AddRectFilled(thumbRect.Min, thumbRect.Max, inScrollBar ? 0xFF606060 : 0xFF505050, 2); | |
} | |
else | |
{ | |
gContext.verticalScroll.Deactivate(); | |
gContext.verticalOffset = 0; | |
} | |
} | |
ImGui::EndGroup(); | |
if (expanded) | |
{ | |
bool overExpanded = SequencerAddDelButton(draw_list, ImVec2(canvas_pos.x + 2, canvas_pos.y + 2), !*expanded); | |
if (overExpanded && io.MouseReleased[0]) | |
*expanded = !*expanded; | |
} | |
if (delEntry != -1) | |
{ | |
sequence->Del(delEntry); | |
if (selectedEntry && (*selectedEntry == delEntry || *selectedEntry >= sequence->GetTrackCount())) | |
{ | |
*selectedEntry = -1; | |
*selectedKey = -1; | |
} | |
} | |
if (dupEntry != -1) | |
{ | |
sequence->Duplicate(dupEntry); | |
} | |
return ret; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
namespace ImSequencer | |
{ | |
enum SEQUENCER_OPTIONS | |
{ | |
SEQUENCER_EDIT_NONE = 0, | |
SEQUENCER_EDIT_STARTEND = 1 << 1, | |
SEQUENCER_CHANGE_FRAME = 1 << 3, | |
SEQUENCER_ADD = 1 << 4, | |
SEQUENCER_DEL = 1 << 5, | |
SEQUENCER_COPYPASTE = 1 << 6, | |
SEQUENCER_EDIT_ALL = SEQUENCER_EDIT_STARTEND | SEQUENCER_CHANGE_FRAME | |
}; | |
enum TRACK_NATURE | |
{ | |
TRACK_NATURE_DEFAULT = 0, | |
TRACK_NATURE_TICK = 1, | |
TRACK_NATURE_SPANNABLE_TICK = 2, | |
}; | |
struct SequenceInterface | |
{ | |
virtual int GetFrameCount() const = 0; | |
virtual int GetTrackCount() const = 0; | |
virtual TRACK_NATURE GetTrackNature(unsigned trackIndex) const = 0; | |
virtual int GetTrackTypeCount() const { return 0; } | |
virtual const char *GetTrackTypeName(int /*typeIndex*/) const { return ""; } | |
virtual const char *GetTrackLabel(int /*index*/) const { return ""; } | |
virtual unsigned GetKeyFrameCount(int trackIndex) = 0; | |
virtual void Get(int trackIndex, int keyIndex, int** start, int** end, int *type, unsigned int *color) = 0; | |
virtual void Add(int /*type*/) {} | |
virtual void Del(int /*index*/) {} | |
virtual void Duplicate(int /*index*/) {} | |
virtual bool SwapKeyframes(int trackIndex, int keyIndex, int withIndex) { return false; } | |
virtual void Copy() {} | |
virtual void Paste() {} | |
}; | |
// return true if selection is made | |
bool Sequencer(SequenceInterface *sequence, int *currentFrame, bool *expanded, int *selectedEntry, int* selectedKey, int *firstFrame, int sequenceOptions); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment