Friends :)
This commit is contained in:
@@ -1041,16 +1041,37 @@ namespace anm2ed::imgui
|
||||
auto isZoomOut = isFocused && shortcut(manager.chords[SHORTCUT_ZOOM_OUT], shortcut::GLOBAL);
|
||||
|
||||
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
|
||||
auto useTool = tool;
|
||||
auto step = (float)(isMod ? STEP_FAST : STEP);
|
||||
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
|
||||
auto selected_frame_references_get = [&]()
|
||||
{
|
||||
std::set<Reference> result = document.frames.references;
|
||||
if (result.empty())
|
||||
for (auto frameIndex : frames)
|
||||
result.insert({reference.animationIndex, reference.itemType, reference.itemID, frameIndex});
|
||||
if (result.empty() && reference.itemType != NONE && reference.frameIndex != -1) result.insert(reference);
|
||||
std::erase_if(result,
|
||||
[](const Reference& frameReference)
|
||||
{ return frameReference.itemType == NONE || frameReference.itemType == TRIGGER; });
|
||||
return result;
|
||||
};
|
||||
auto selectedFrameReferences = selected_frame_references_get();
|
||||
auto editReference = reference;
|
||||
auto editItemType = referenceItemType;
|
||||
if ((referenceItemType == ItemType::TRIGGER || !selectedFrameReferences.contains(reference)) &&
|
||||
!selectedFrameReferences.empty())
|
||||
{
|
||||
editReference = *selectedFrameReferences.begin();
|
||||
editItemType = static_cast<ItemType>(editReference.itemType);
|
||||
}
|
||||
|
||||
auto frame =
|
||||
anm2.element_get(reference.animationIndex, referenceItemType, reference.frameIndex, reference.itemID);
|
||||
auto item = anm2.element_get(reference.animationIndex, referenceItemType, reference.itemID);
|
||||
auto useTool = tool;
|
||||
auto step = isMod ? STEP_FAST : STEP;
|
||||
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
|
||||
anm2.element_get(editReference.animationIndex, editItemType, editReference.frameIndex, editReference.itemID);
|
||||
auto item = anm2.element_get(editReference.animationIndex, editItemType, editReference.itemID);
|
||||
auto nulls = anm2.element_get(ElementType::NULLS);
|
||||
auto selectedNull = referenceItemType == ItemType::NULL_ && nulls
|
||||
? element_child_id_get(*nulls, ElementType::NULL_ELEMENT, reference.itemID)
|
||||
auto selectedNull = editItemType == ItemType::NULL_ && nulls
|
||||
? element_child_id_get(*nulls, ElementType::NULL_ELEMENT, editReference.itemID)
|
||||
: nullptr;
|
||||
bool isSelectedNullRect = selectedNull && selectedNull->isShowRect;
|
||||
auto null_rect_top_left = [](const Element& frame) { return frame.position - (frame.scale * 0.5f); };
|
||||
@@ -1081,19 +1102,37 @@ namespace anm2ed::imgui
|
||||
};
|
||||
auto frame_change_apply = [&](FrameChange frameChange, ChangeType changeType = ChangeType::ADJUST)
|
||||
{
|
||||
auto queuedReference = reference;
|
||||
auto queuedReferenceItemType = referenceItemType;
|
||||
auto queuedFrames = frames;
|
||||
auto queuedFrameReferences = selectedFrameReferences;
|
||||
manager.command_push({manager.selected,
|
||||
[=](Manager&, Document& document)
|
||||
{
|
||||
auto item = document.anm2.element_get(queuedReference.animationIndex,
|
||||
queuedReferenceItemType,
|
||||
queuedReference.itemID);
|
||||
if (!item) return;
|
||||
frames_change(*item, frameChange, queuedReferenceItemType, changeType, queuedFrames);
|
||||
std::map<Reference, std::set<int>> groupedFrames{};
|
||||
for (auto frameReference : queuedFrameReferences)
|
||||
groupedFrames[{frameReference.animationIndex, frameReference.itemType,
|
||||
frameReference.itemID, -1}]
|
||||
.insert(frameReference.frameIndex);
|
||||
|
||||
for (auto& [itemReference, itemFrames] : groupedFrames)
|
||||
{
|
||||
auto itemType = static_cast<ItemType>(itemReference.itemType);
|
||||
auto item = document.anm2.element_get(itemReference.animationIndex, itemType,
|
||||
itemReference.itemID);
|
||||
if (!item) continue;
|
||||
frames_change(*item, frameChange, itemType, changeType, itemFrames);
|
||||
}
|
||||
}});
|
||||
};
|
||||
auto frame_position_apply = [&](vec2 position)
|
||||
{
|
||||
if (!frame) return;
|
||||
frame_change_apply({.positionX = position.x - frame->position.x, .positionY = position.y - frame->position.y},
|
||||
ChangeType::ADD);
|
||||
};
|
||||
auto frame_scale_apply = [&](vec2 scale)
|
||||
{
|
||||
if (!frame) return;
|
||||
frame_change_apply({.scaleX = scale.x - frame->scale.x, .scaleY = scale.y - frame->scale.y}, ChangeType::ADD);
|
||||
};
|
||||
auto frames_changed = [&]()
|
||||
{
|
||||
manager.command_push(
|
||||
@@ -1103,10 +1142,11 @@ namespace anm2ed::imgui
|
||||
{
|
||||
topLeft = vec2(ivec2(topLeft));
|
||||
rectSize = vec2(ivec2(rectSize));
|
||||
frame_change_apply({.positionX = topLeft.x + rectSize.x * 0.5f,
|
||||
.positionY = topLeft.y + rectSize.y * 0.5f,
|
||||
.scaleX = rectSize.x,
|
||||
.scaleY = rectSize.y});
|
||||
frame_change_apply({.positionX = topLeft.x + rectSize.x * 0.5f - frame->position.x,
|
||||
.positionY = topLeft.y + rectSize.y * 0.5f - frame->position.y,
|
||||
.scaleX = rectSize.x - frame->scale.x,
|
||||
.scaleY = rectSize.y - frame->scale.y},
|
||||
ChangeType::ADD);
|
||||
};
|
||||
|
||||
auto& toolInfo = tool::INFO[useTool];
|
||||
@@ -1125,7 +1165,7 @@ namespace anm2ed::imgui
|
||||
if (isMouseDown || isMouseMiddleDown) pan += vec2(mouseDelta.x, mouseDelta.y);
|
||||
break;
|
||||
case tool::MOVE:
|
||||
if (!item || !frame || frames.empty()) break;
|
||||
if (!item || !frame || selectedFrameReferences.empty()) break;
|
||||
if (isToolBegin)
|
||||
{
|
||||
frame_snapshot(EDIT_FRAME_POSITION);
|
||||
@@ -1140,10 +1180,10 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto position = mousePos - moveOffset;
|
||||
if (isSelectedNullRect)
|
||||
frame_change_apply({.positionX = (int)(position.x + frame->scale.x * 0.5f),
|
||||
.positionY = (int)(position.y + frame->scale.y * 0.5f)});
|
||||
frame_position_apply(vec2((float)(int)(position.x + frame->scale.x * 0.5f),
|
||||
(float)(int)(position.y + frame->scale.y * 0.5f)));
|
||||
else
|
||||
frame_change_apply({.positionX = (int)position.x, .positionY = (int)position.y});
|
||||
frame_position_apply(vec2((float)(int)position.x, (float)(int)position.y));
|
||||
}
|
||||
|
||||
if (isLeftPressed) frame_change_apply({.positionX = step}, ChangeType::SUBTRACT);
|
||||
@@ -1165,7 +1205,7 @@ namespace anm2ed::imgui
|
||||
}
|
||||
break;
|
||||
case tool::SCALE:
|
||||
if (!item || !frame || frames.empty()) break;
|
||||
if (!item || !frame || selectedFrameReferences.empty()) break;
|
||||
if (isToolBegin)
|
||||
{
|
||||
frame_snapshot(EDIT_FRAME_SCALE);
|
||||
@@ -1192,7 +1232,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto scale = frame->scale + vec2(mouseDelta.x, mouseDelta.y);
|
||||
if (isMod) scale = {scale.x, scale.x};
|
||||
frame_change_apply({.scaleX = scale.x, .scaleY = scale.y});
|
||||
frame_scale_apply(scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1237,9 +1277,9 @@ namespace anm2ed::imgui
|
||||
}
|
||||
break;
|
||||
case tool::ROTATE:
|
||||
if (!item || !frame || frames.empty()) break;
|
||||
if (!item || !frame || selectedFrameReferences.empty()) break;
|
||||
if (isToolBegin) frame_snapshot(EDIT_FRAME_ROTATION);
|
||||
if (isToolMouseDown) frame_change_apply({.rotation = (int)mouseDelta.x}, ChangeType::ADD);
|
||||
if (isToolMouseDown) frame_change_apply({.rotation = (float)(int)mouseDelta.x}, ChangeType::ADD);
|
||||
if (isLeftPressed || isDownPressed) frame_change_apply({.rotation = step}, ChangeType::SUBTRACT);
|
||||
if (isUpPressed || isRightPressed) frame_change_apply({.rotation = step}, ChangeType::ADD);
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ namespace anm2ed::imgui
|
||||
bool isReferenceLayerOnSpritesheet =
|
||||
frame && reference.itemID > -1 && layer && layer->spritesheetId == referenceSpritesheet;
|
||||
auto useTool = tool;
|
||||
auto step = isMod ? STEP_FAST : STEP;
|
||||
auto step = (float)(isMod ? STEP_FAST : STEP);
|
||||
auto stepX = isGridSnap ? step * gridSize.x : step;
|
||||
auto stepY = isGridSnap ? step * gridSize.y : step;
|
||||
previousMousePos = mousePos;
|
||||
@@ -692,10 +692,10 @@ namespace anm2ed::imgui
|
||||
if (isMouseDown)
|
||||
{
|
||||
auto frameCrop = ivec2(frame->crop);
|
||||
frame_change_apply({.pivotX = (int)(mousePos.x - frameCrop.x),
|
||||
.pivotY = (int)(mousePos.y - frameCrop.y),
|
||||
.cropX = frameCrop.x,
|
||||
.cropY = frameCrop.y});
|
||||
frame_change_apply({.pivotX = (float)(int)(mousePos.x - frameCrop.x),
|
||||
.pivotY = (float)(int)(mousePos.y - frameCrop.y),
|
||||
.cropX = (float)frameCrop.x,
|
||||
.cropY = (float)frameCrop.y});
|
||||
}
|
||||
if (isLeftPressed) frame_change_apply({.pivotX = step}, ChangeType::SUBTRACT);
|
||||
if (isRightPressed) frame_change_apply({.pivotX = step}, ChangeType::ADD);
|
||||
@@ -703,7 +703,8 @@ namespace anm2ed::imgui
|
||||
if (isDownPressed) frame_change_apply({.pivotY = step}, ChangeType::ADD);
|
||||
|
||||
frame_change_from_current_apply(
|
||||
[](const Element& frame) { return FrameChange{.pivotX = (int)frame.pivot.x, .pivotY = (int)frame.pivot.y}; });
|
||||
[](const Element& frame)
|
||||
{ return FrameChange{.pivotX = (float)(int)frame.pivot.x, .pivotY = (float)(int)frame.pivot.y}; });
|
||||
|
||||
if (isDuring)
|
||||
{
|
||||
@@ -770,7 +771,8 @@ namespace anm2ed::imgui
|
||||
if (isMouseClicked)
|
||||
{
|
||||
cropAnchor = mousePos;
|
||||
frame_change_apply({.cropX = (int)cropAnchor.x, .cropY = (int)cropAnchor.y, .sizeX = {}, .sizeY = {}});
|
||||
frame_change_apply(
|
||||
{.cropX = (float)(int)cropAnchor.x, .cropY = (float)(int)cropAnchor.y, .sizeX = {}, .sizeY = {}});
|
||||
}
|
||||
if (isMouseDown)
|
||||
{
|
||||
|
||||
+142
-92
@@ -973,15 +973,14 @@ namespace anm2ed::imgui
|
||||
auto row_drag_references_get = [&](const TimelineItemRow& row)
|
||||
{
|
||||
auto clicked = row_reference_get(row);
|
||||
if (clicked.type != LAYER && clicked.type != NULL_) return std::vector<TimelineRowReference>{clicked};
|
||||
|
||||
auto rows = selected_row_references_get();
|
||||
if (!is_row_reference_selected(clicked)) rows = {clicked};
|
||||
if (rows.empty()) rows = {clicked};
|
||||
|
||||
auto type = rows.front().type;
|
||||
if (type != LAYER && type != NULL_) return std::vector<TimelineRowReference>{clicked};
|
||||
for (auto rowReference : rows)
|
||||
if (rowReference.type != type || (rowReference.type != LAYER && rowReference.type != NULL_))
|
||||
return std::vector<TimelineRowReference>{clicked};
|
||||
std::erase_if(rows, [&](const TimelineRowReference& rowReference) { return rowReference.type != clicked.type; });
|
||||
if (rows.empty()) rows = {clicked};
|
||||
return rows;
|
||||
};
|
||||
|
||||
@@ -1289,7 +1288,8 @@ namespace anm2ed::imgui
|
||||
auto item = item_get(frameReference.itemType, frameReference.itemID);
|
||||
auto frame = item ? track_frame_get(*item, frameReference.frameIndex) : nullptr;
|
||||
if (!frame) continue;
|
||||
clipboardString += element_to_string(*frame);
|
||||
auto parentType = item_type_to_track_type_get(item_type_get(frameReference.itemType));
|
||||
clipboardString += element_to_string(*frame, parentType);
|
||||
}
|
||||
if (!clipboardString.empty()) clipboard.set(clipboardString);
|
||||
};
|
||||
@@ -2334,6 +2334,38 @@ namespace anm2ed::imgui
|
||||
return leftMin.x <= rightMax.x && leftMax.x >= rightMin.x && leftMin.y <= rightMax.y &&
|
||||
leftMax.y >= rightMin.y;
|
||||
};
|
||||
auto frame_overlay_draw = [&](ImDrawList* drawList, ImVec2 clipMin, ImVec2 clipMax)
|
||||
{
|
||||
if (isFrameBoxSelecting && isFrameBoxClipSet)
|
||||
{
|
||||
auto boxContentMin =
|
||||
ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxContentMax =
|
||||
ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxMin = frame_box_screen_point_get(boxContentMin);
|
||||
auto boxMax = frame_box_screen_point_get(boxContentMax);
|
||||
auto boxClipMin = clipMin;
|
||||
if (isPlayheadLineSet) boxClipMin.y = std::max(boxClipMin.y, playheadLineTopY);
|
||||
drawList->PushClipRect(boxClipMin, clipMax, true);
|
||||
drawList->AddRectFilled(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTargetBg));
|
||||
drawList->AddRect(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTarget));
|
||||
drawList->PopClipRect();
|
||||
}
|
||||
|
||||
if (isPlayheadLineSet)
|
||||
{
|
||||
auto lineTopY = std::max(clipMin.y, playheadLineTopY);
|
||||
if (clipMax.y > lineTopY)
|
||||
{
|
||||
auto linePos = ImVec2(playheadLineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), lineTopY);
|
||||
auto lineSize = ImVec2(PLAYHEAD_LINE_THICKNESS * 0.5f, clipMax.y - lineTopY);
|
||||
drawList->PushClipRect(clipMin, clipMax, true);
|
||||
drawList->AddRectFilled(linePos, ImVec2(linePos.x + lineSize.x, linePos.y + lineSize.y),
|
||||
ImGui::GetColorU32(playheadLineColor));
|
||||
drawList->PopClipRect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto frame_child = [&](const TimelineItemRow& row, int& index, float width)
|
||||
{
|
||||
@@ -2382,7 +2414,40 @@ namespace anm2ed::imgui
|
||||
bool isDefaultChild = type == NONE;
|
||||
if (isLightTheme && isDefaultChild) ImGui::PushStyleColor(ImGuiCol_ChildBg, TIMELINE_CHILD_BG_COLOR_LIGHT);
|
||||
|
||||
if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders))
|
||||
bool isFramesChildVisible = ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders);
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
auto clipMax = drawList->GetClipRectMax();
|
||||
auto length = animation ? animation->frameNum : 0;
|
||||
auto frameSize = ImVec2(ImGui::GetTextLineHeight(), ImGui::GetContentRegionAvail().y);
|
||||
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
auto border = glm::max(0.5f, ImGui::GetStyle().FrameBorderSize * 0.5f);
|
||||
auto borderLineLength = frameSize.y / 5;
|
||||
auto frameMin = std::max(0, (int)std::floor(scroll.x / frameSize.x) - 1);
|
||||
auto frameMax = std::min(FRAME_NUM_MAX, (int)std::ceil((scroll.x + clipMax.x) / frameSize.x) + 1);
|
||||
|
||||
if (isFrameBoxSelecting && isFrameBoxClipSet && type != NONE && animation && item)
|
||||
{
|
||||
auto boxMin = ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxMax = ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto rowMinY = cursorScreenPos.y + scroll.y - frameBoxClipMin.y;
|
||||
auto rowMaxY = rowMinY + childSize.y;
|
||||
float selectionFrameTime{};
|
||||
for (auto [i, frame] : std::views::enumerate(item->children))
|
||||
{
|
||||
auto frameReference = Reference{reference.animationIndex, type, id, (int)i};
|
||||
auto frameStart = type == TRIGGER ? frame.atFrame : selectionFrameTime;
|
||||
auto frameEnd = type == TRIGGER ? frameStart + 1.0f : frameStart + frame.duration;
|
||||
auto frameContentMin = ImVec2(frameStart * frameSize.x, rowMinY);
|
||||
auto frameContentMax = ImVec2(frameEnd * frameSize.x, rowMaxY);
|
||||
if (is_frame_box_overlapping(frameContentMin, frameContentMax, boxMin, boxMax))
|
||||
frameBoxSelection.insert(frameReference);
|
||||
if (type != TRIGGER) selectionFrameTime += frame.duration;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFramesChildVisible)
|
||||
{
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||
ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteFocused))
|
||||
@@ -2396,19 +2461,6 @@ namespace anm2ed::imgui
|
||||
reference_clear();
|
||||
}
|
||||
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
auto clipMax = drawList->GetClipRectMax();
|
||||
auto length = animation ? animation->frameNum : 0;
|
||||
auto frameSize = ImVec2(ImGui::GetTextLineHeight(), ImGui::GetContentRegionAvail().y);
|
||||
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
auto border = glm::max(0.5f, ImGui::GetStyle().FrameBorderSize * 0.5f);
|
||||
auto borderLineLength = frameSize.y / 5;
|
||||
auto frameMin = std::max(0, (int)std::floor(scroll.x / frameSize.x) - 1);
|
||||
auto frameMax = std::min(FRAME_NUM_MAX, (int)std::ceil((scroll.x + clipMax.x) / frameSize.x) + 1);
|
||||
pickerLineDrawList = drawList;
|
||||
|
||||
if (type == NONE)
|
||||
{
|
||||
if (length > 0)
|
||||
@@ -2495,7 +2547,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
float frameTime{};
|
||||
|
||||
if (!isFrameBoxSelecting && !frameMoveDrag.isActive && ImGui::IsWindowHovered() &&
|
||||
if (!isFrameBoxPending && !isFrameBoxSelecting && !frameMoveDrag.isActive && ImGui::IsWindowHovered() &&
|
||||
(ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsMouseReleased(ImGuiMouseButton_Right)) &&
|
||||
!ImGui::IsAnyItemHovered())
|
||||
reference_set_item(type, id);
|
||||
@@ -2515,7 +2567,7 @@ namespace anm2ed::imgui
|
||||
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) &&
|
||||
ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsAnyItemHovered())
|
||||
{
|
||||
isFrameBoxSelecting = true;
|
||||
isFrameBoxPending = true;
|
||||
isFrameBoxAdditive = ImGui::IsKeyDown(ImGuiMod_Ctrl);
|
||||
frameBoxStart = frame_box_content_point_get();
|
||||
frameBoxEnd = frameBoxStart;
|
||||
@@ -2595,16 +2647,6 @@ namespace anm2ed::imgui
|
||||
auto buttonSize = type == TRIGGER ? frameSize : to_imvec2(vec2(frameSize.x * frame.duration, frameSize.y));
|
||||
auto frameStart = type == TRIGGER ? frame.atFrame : frameTime;
|
||||
auto frameEnd = type == TRIGGER ? frameStart + 1.0f : frameStart + frame.duration;
|
||||
if (isFrameBoxSelecting && isFrameBoxClipSet)
|
||||
{
|
||||
auto boxMin = ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxMax = ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto rowMinY = cursorScreenPos.y + scroll.y - frameBoxClipMin.y;
|
||||
auto frameContentMin = ImVec2(frameStart * frameSize.x, rowMinY);
|
||||
auto frameContentMax = ImVec2(frameEnd * frameSize.x, rowMinY + frameSize.y);
|
||||
if (is_frame_box_overlapping(frameContentMin, frameContentMax, boxMin, boxMax))
|
||||
frameBoxSelection.insert(frameReference);
|
||||
}
|
||||
if (frameEnd <= (float)frameMin || frameStart >= (float)frameMax)
|
||||
{
|
||||
if (type != TRIGGER) frameTime += frame.duration;
|
||||
@@ -2692,7 +2734,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Left))
|
||||
if (!isDraggedFrameActive && ImGui::IsItemHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Left))
|
||||
{
|
||||
if (ImGui::IsMouseDown(ImGuiMouseButton_Left))
|
||||
{
|
||||
@@ -2704,6 +2746,8 @@ namespace anm2ed::imgui
|
||||
draggedFrameIndex = (int)i;
|
||||
draggedFrameStart = hoveredTime;
|
||||
if (type != TRIGGER) draggedFrameStartDuration = frame.duration;
|
||||
draggedFrameStartMouseX = ImGui::GetIO().MousePos.x;
|
||||
draggedFrameWidth = frameSize.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2816,13 +2860,20 @@ namespace anm2ed::imgui
|
||||
frameSelectionSnapshotReference = reference;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (isDraggedFrameActive)
|
||||
{
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
auto durationDelta = draggedFrameWidth > 0.0f
|
||||
? static_cast<int>((ImGui::GetIO().MousePos.x - draggedFrameStartMouseX) /
|
||||
draggedFrameWidth)
|
||||
: hoveredTime - draggedFrameStart;
|
||||
auto isDraggedFrameChanged =
|
||||
draggedFrameType == TRIGGER ? hoveredTime != draggedFrameStart : durationDelta != 0;
|
||||
|
||||
if (!isDraggedFrameSnapshot && hoveredTime != draggedFrameStart)
|
||||
if (!isDraggedFrameSnapshot && isDraggedFrameChanged)
|
||||
{
|
||||
isDraggedFrameSnapshot = true;
|
||||
snapshot_command_push(draggedFrameType == TRIGGER ? EDIT_TRIGGER_AT_FRAME : EDIT_FRAME_DURATION);
|
||||
@@ -2833,9 +2884,9 @@ namespace anm2ed::imgui
|
||||
auto targetReference = draggedFrameReference;
|
||||
auto targetType = draggedFrameType;
|
||||
auto targetIndex = draggedFrameIndex;
|
||||
auto targetStart = draggedFrameStart;
|
||||
auto targetStartDuration = draggedFrameStartDuration;
|
||||
auto targetHoveredTime = hoveredTime;
|
||||
auto targetDurationDelta = durationDelta;
|
||||
auto isPlaybackClamp = settings.playbackIsClamp;
|
||||
auto animationLength = animation ? animation->frameNum : FRAME_NUM_MAX;
|
||||
command_push([=](Manager&, Document& document)
|
||||
@@ -2859,8 +2910,8 @@ namespace anm2ed::imgui
|
||||
}
|
||||
else
|
||||
{
|
||||
frame->duration = glm::clamp(targetStartDuration + (targetHoveredTime - targetStart),
|
||||
FRAME_DURATION_MIN, FRAME_DURATION_MAX);
|
||||
frame->duration = glm::clamp(targetStartDuration + targetDurationDelta, FRAME_DURATION_MIN,
|
||||
FRAME_DURATION_MAX);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2882,6 +2933,8 @@ namespace anm2ed::imgui
|
||||
draggedFrameIndex = -1;
|
||||
draggedFrameStart = -1;
|
||||
draggedFrameStartDuration = -1;
|
||||
draggedFrameStartMouseX = 0.0f;
|
||||
draggedFrameWidth = 0.0f;
|
||||
isDraggedFrameSnapshot = false;
|
||||
frameSelectionLocked.clear();
|
||||
}
|
||||
@@ -2971,23 +3024,34 @@ namespace anm2ed::imgui
|
||||
frameBoxClipMin = frameBoxDrawList->GetClipRectMin();
|
||||
frameBoxClipMax = frameBoxDrawList->GetClipRectMax();
|
||||
isFrameBoxClipSet = true;
|
||||
if (isFrameBoxSelecting)
|
||||
if (isFrameBoxPending || isFrameBoxSelecting)
|
||||
{
|
||||
auto& io = ImGui::GetIO();
|
||||
auto threshold = ImGui::GetTextLineHeightWithSpacing() * 0.125f;
|
||||
frameBoxEnd = frame_box_content_point_get();
|
||||
auto distance = ImVec2(frameBoxEnd.x - frameBoxStart.x, frameBoxEnd.y - frameBoxStart.y);
|
||||
if (isFrameBoxPending && distance.x * distance.x + distance.y * distance.y >= threshold * threshold)
|
||||
{
|
||||
isFrameBoxPending = false;
|
||||
isFrameBoxSelecting = true;
|
||||
}
|
||||
auto edgeSize = ImGui::GetTextLineHeightWithSpacing();
|
||||
auto scrollStep = edgeSize * 0.5f;
|
||||
if (io.MousePos.x < frameBoxClipMin.x + edgeSize)
|
||||
scroll.x -= scrollStep;
|
||||
else if (io.MousePos.x > frameBoxClipMax.x - edgeSize)
|
||||
scroll.x += scrollStep;
|
||||
if (io.MousePos.y < frameBoxClipMin.y + edgeSize)
|
||||
scroll.y -= scrollStep;
|
||||
else if (io.MousePos.y > frameBoxClipMax.y - edgeSize)
|
||||
scroll.y += scrollStep;
|
||||
ImGui::SetScrollX(scroll.x);
|
||||
ImGui::SetScrollY(scroll.y);
|
||||
frameBoxEnd = frame_box_content_point_get();
|
||||
frameBoxSelection.clear();
|
||||
if (isFrameBoxSelecting)
|
||||
{
|
||||
if (io.MousePos.x < frameBoxClipMin.x + edgeSize)
|
||||
scroll.x -= scrollStep;
|
||||
else if (io.MousePos.x > frameBoxClipMax.x - edgeSize)
|
||||
scroll.x += scrollStep;
|
||||
if (io.MousePos.y < frameBoxClipMin.y + edgeSize)
|
||||
scroll.y -= scrollStep;
|
||||
else if (io.MousePos.y > frameBoxClipMax.y - edgeSize)
|
||||
scroll.y += scrollStep;
|
||||
ImGui::SetScrollX(scroll.x);
|
||||
ImGui::SetScrollY(scroll.y);
|
||||
frameBoxEnd = frame_box_content_point_get();
|
||||
frameBoxSelection.clear();
|
||||
}
|
||||
}
|
||||
|
||||
int index{};
|
||||
@@ -3016,53 +3080,39 @@ namespace anm2ed::imgui
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImDrawList* windowDrawList = ImGui::GetWindowDrawList();
|
||||
ImDrawList* foregroundDrawList = ImGui::GetForegroundDrawList();
|
||||
auto lineBottomY = ImGui::GetWindowPos().y + ImGui::GetWindowSize().y;
|
||||
if (isHorizontalScroll) lineBottomY -= ImGui::GetStyle().ScrollbarSize;
|
||||
|
||||
auto rectMin = windowDrawList->GetClipRectMin();
|
||||
auto rectMax = windowDrawList->GetClipRectMax();
|
||||
if (isFrameBoxSelecting && isFrameBoxClipSet)
|
||||
if (isFrameBoxClipSet)
|
||||
{
|
||||
auto boxContentMin =
|
||||
ImVec2(std::min(frameBoxStart.x, frameBoxEnd.x), std::min(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxContentMax =
|
||||
ImVec2(std::max(frameBoxStart.x, frameBoxEnd.x), std::max(frameBoxStart.y, frameBoxEnd.y));
|
||||
auto boxMin = frame_box_screen_point_get(boxContentMin);
|
||||
auto boxMax = frame_box_screen_point_get(boxContentMax);
|
||||
auto boxClipMin = rectMin;
|
||||
if (isPlayheadLineSet) boxClipMin.y = std::max(boxClipMin.y, playheadLineTopY);
|
||||
foregroundDrawList->PushClipRect(boxClipMin, rectMax, true);
|
||||
foregroundDrawList->AddRectFilled(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTargetBg));
|
||||
foregroundDrawList->AddRect(boxMin, boxMax, ImGui::GetColorU32(ImGuiCol_DragDropTarget));
|
||||
foregroundDrawList->PopClipRect();
|
||||
auto overlayCursor = ImGui::GetCursorScreenPos();
|
||||
auto overlayPos = ImGui::GetWindowPos();
|
||||
auto overlaySize = ImGui::GetWindowSize();
|
||||
ImGui::SetCursorScreenPos(overlayPos);
|
||||
if (ImGui::BeginChild("##Frames Overlay Child", overlaySize, ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
|
||||
ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoNavInputs))
|
||||
frame_overlay_draw(ImGui::GetWindowDrawList(), frameBoxClipMin, frameBoxClipMax);
|
||||
ImGui::EndChild();
|
||||
ImGui::SetCursorScreenPos(overlayCursor);
|
||||
}
|
||||
|
||||
if (pickerLineDrawList && isPlayheadLineSet && lineBottomY > playheadLineTopY)
|
||||
{
|
||||
auto linePos = ImVec2(playheadLineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), playheadLineTopY);
|
||||
auto lineSize = ImVec2((PLAYHEAD_LINE_THICKNESS / 2.0f), std::max(0.0f, lineBottomY - playheadLineTopY));
|
||||
foregroundDrawList->PushClipRect(rectMin, rectMax, true);
|
||||
foregroundDrawList->AddRectFilled(linePos, ImVec2(linePos.x + lineSize.x, linePos.y + lineSize.y),
|
||||
ImGui::GetColorU32(playheadLineColor));
|
||||
foregroundDrawList->PopClipRect();
|
||||
}
|
||||
|
||||
if (isFrameBoxSelecting)
|
||||
if (isFrameBoxPending || isFrameBoxSelecting)
|
||||
{
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left))
|
||||
{
|
||||
group_selection_reset();
|
||||
if (isFrameBoxAdditive)
|
||||
document.frames.references.insert(frameBoxSelection.begin(), frameBoxSelection.end());
|
||||
else
|
||||
document.frames.references = frameBoxSelection;
|
||||
document.items.references.clear();
|
||||
for (auto frameReference : document.frames.references)
|
||||
document.items.references.insert(item_reference_from_frame_get(frameReference));
|
||||
if (!document.frames.references.empty()) reference = *document.frames.references.begin();
|
||||
frames_selection_sync_for(document);
|
||||
if (isFrameBoxSelecting)
|
||||
{
|
||||
group_selection_reset();
|
||||
if (isFrameBoxAdditive)
|
||||
document.frames.references.insert(frameBoxSelection.begin(), frameBoxSelection.end());
|
||||
else
|
||||
document.frames.references = frameBoxSelection;
|
||||
document.items.references.clear();
|
||||
for (auto frameReference : document.frames.references)
|
||||
document.items.references.insert(item_reference_from_frame_get(frameReference));
|
||||
if (!document.frames.references.empty()) reference = *document.frames.references.begin();
|
||||
frames_selection_sync_for(document);
|
||||
}
|
||||
isFrameBoxPending = false;
|
||||
isFrameBoxSelecting = false;
|
||||
frameBoxSelection.clear();
|
||||
}
|
||||
@@ -3240,7 +3290,7 @@ namespace anm2ed::imgui
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::End();
|
||||
|
||||
itemProperties.update(manager, settings, document, reference, reference_set_item);
|
||||
if (itemProperties.update(manager, settings, document, reference)) group_selection_reset();
|
||||
group_properties_update();
|
||||
|
||||
bakePopup.trigger();
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace anm2ed::imgui
|
||||
popup::ItemProperties itemProperties{};
|
||||
PopupHelper bakePopup{PopupHelper(LABEL_TIMELINE_BAKE_POPUP, POPUP_SMALL_NO_HEIGHT)};
|
||||
int hoveredTime{};
|
||||
bool isFrameBoxPending{};
|
||||
bool isFrameBoxSelecting{};
|
||||
bool isFrameBoxAdditive{};
|
||||
ImVec2 frameBoxStart{};
|
||||
@@ -75,6 +76,8 @@ namespace anm2ed::imgui
|
||||
int draggedFrameIndex{-1};
|
||||
int draggedFrameStart{-1};
|
||||
int draggedFrameStartDuration{-1};
|
||||
float draggedFrameStartMouseX{};
|
||||
float draggedFrameWidth{};
|
||||
bool isDraggedFrameSnapshot{};
|
||||
bool frameFocusRequested{};
|
||||
int frameFocusIndex{-1};
|
||||
@@ -87,7 +90,6 @@ namespace anm2ed::imgui
|
||||
bool isFrameSelectionAnchorSet{};
|
||||
std::vector<TimelineRowReference> rowDragReferences{};
|
||||
glm::vec2 scroll{};
|
||||
ImDrawList* pickerLineDrawList{};
|
||||
ImGuiStyle style{};
|
||||
|
||||
public:
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace anm2ed::imgui
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse))
|
||||
{
|
||||
|
||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
|
||||
ImGui::TextUnformatted(localize.get(LABEL_APPLICATION_NAME));
|
||||
ImGui::PopFont();
|
||||
|
||||
@@ -292,8 +292,8 @@ namespace anm2ed::imgui
|
||||
|
||||
std::filesystem::path window_asset_path_get(Document& document, const std::filesystem::path& path)
|
||||
{
|
||||
WorkingDirectory workingDirectory(document.directory_get());
|
||||
return path::lower_case_backslash_handle(path::make_relative(path));
|
||||
auto loadPath = path::lower_case_backslash_handle(path);
|
||||
return path::backslash_replace(path::make_relative(loadPath, document.directory_get()));
|
||||
}
|
||||
|
||||
void window_directory_open(Dialog& dialog, Document& document, const std::filesystem::path& path)
|
||||
@@ -1900,7 +1900,12 @@ namespace anm2ed::imgui
|
||||
window.reload = [](Window& window, Manager&, Settings&, Document& document, Clipboard&)
|
||||
{
|
||||
auto selected = document.spritesheet.selection;
|
||||
window_edit(window, document, localize.get(EDIT_RELOAD_SPRITESHEETS), []() {});
|
||||
window_edit(window, document, localize.get(EDIT_RELOAD_SPRITESHEETS),
|
||||
[&]()
|
||||
{
|
||||
for (auto& id : selected)
|
||||
document.texture_reload(id);
|
||||
});
|
||||
|
||||
for (auto& id : selected)
|
||||
{
|
||||
@@ -2286,6 +2291,7 @@ namespace anm2ed::imgui
|
||||
auto spritesheet = document.anm2.element_get(ElementType::SPRITESHEET, id);
|
||||
if (!spritesheet) return;
|
||||
spritesheet->path = window_asset_path_get(document, dialogPath);
|
||||
document.texture_reload(id);
|
||||
};
|
||||
|
||||
window_edit(document, Document::SPRITESHEETS, localize.get(EDIT_REPLACE_SPRITESHEET),
|
||||
@@ -2544,6 +2550,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto sound = window_element_get(window, document.anm2, id);
|
||||
if (!sound) continue;
|
||||
document.sound_reload(id);
|
||||
auto pathString = path::to_utf8(sound->path);
|
||||
toasts.push(std::vformat(localize.get(TOAST_RELOAD_SOUND), std::make_format_args(id, pathString)));
|
||||
logger.info(
|
||||
@@ -2850,6 +2857,7 @@ namespace anm2ed::imgui
|
||||
auto sound = document.anm2.element_get(ElementType::SOUND_ELEMENT, id);
|
||||
if (!sound) return;
|
||||
sound->path = window_asset_path_get(document, dialogPath);
|
||||
document.sound_reload(id);
|
||||
auto pathString = path::to_utf8(sound->path);
|
||||
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SOUND), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REPLACE_SOUND, anm2ed::ENGLISH),
|
||||
|
||||
Reference in New Issue
Block a user