From 7db835e6d4d98bc48508f6b686a127dfd22bed64 Mon Sep 17 00:00:00 2001 From: shweet Date: Wed, 20 May 2026 11:41:40 -0400 Subject: [PATCH] managed autosave deletion --- src/imgui/window/regions.cpp | 133 ++++++++++++++++---------- src/imgui/window/timeline.cpp | 30 +++--- src/imgui/wizard/render_animation.cpp | 2 +- src/manager.cpp | 57 +++++++---- src/manager.hpp | 2 +- 5 files changed, 139 insertions(+), 85 deletions(-) diff --git a/src/imgui/window/regions.cpp b/src/imgui/window/regions.cpp index e91b6d2..07863a5 100644 --- a/src/imgui/window/regions.cpp +++ b/src/imgui/window/regions.cpp @@ -271,6 +271,8 @@ namespace anm2ed::imgui bool isValid = spritesheet->is_valid(); auto& texture = isValid ? spritesheet->texture : resources.icons[icon::NONE]; auto tintColor = !isValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); + static std::vector dragDropSelection{}; + bool isRegionDragDropSourceSubmitted = false; for (int i = 0; i < (int)spritesheet->regionOrder.size(); i++) { @@ -324,78 +326,75 @@ namespace anm2ed::imgui auto tooltipPadding = style.WindowPadding.x * 4.0f; auto minWidth = previewSize.x + style.ItemSpacing.x + textWidth + tooltipPadding; - ImGui::SetNextWindowSize(ImVec2(minWidth, 0), ImGuiCond_Appearing); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding); - if (ImGui::BeginItemTooltip()) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip) && !ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); - auto childFlags = ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY; - auto noScrollFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; - - if (ImGui::BeginChild("##Region Tooltip Image Child", to_imvec2(previewSize), childFlags, noScrollFlags)) - ImGui::ImageWithBg(texture.id, to_imvec2(previewSize), to_imvec2(uvMin), to_imvec2(uvMax), ImVec4(), - tintColor); - ImGui::EndChild(); - ImGui::PopStyleVar(); - - ImGui::SameLine(); - - auto infoChildFlags = ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY; - if (ImGui::BeginChild("##Region Info Tooltip Child", ImVec2(), infoChildFlags, noScrollFlags)) + ImGui::SetNextWindowSize(ImVec2(minWidth, 0), ImGuiCond_Appearing); + if (ImGui::BeginItemTooltip()) { - ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); - ImGui::TextUnformatted(nameCStr); - ImGui::PopFont(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); + auto childFlags = ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY; + auto noScrollFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; - ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str()); - ImGui::TextUnformatted( - std::vformat(localize.get(FORMAT_CROP), std::make_format_args(region.crop.x, region.crop.y)) - .c_str()); - ImGui::TextUnformatted( - std::vformat(localize.get(FORMAT_SIZE), std::make_format_args(region.size.x, region.size.y)) - .c_str()); - if (region.origin == anm2::Spritesheet::Region::CUSTOM) + if (ImGui::BeginChild("##Region Tooltip Image Child", to_imvec2(previewSize), childFlags, + noScrollFlags)) + ImGui::ImageWithBg(texture.id, to_imvec2(previewSize), to_imvec2(uvMin), to_imvec2(uvMax), ImVec4(), + tintColor); + ImGui::EndChild(); + ImGui::PopStyleVar(); + + ImGui::SameLine(); + + auto infoChildFlags = ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY; + if (ImGui::BeginChild("##Region Info Tooltip Child", ImVec2(), infoChildFlags, noScrollFlags)) { + ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); + ImGui::TextUnformatted(nameCStr); + ImGui::PopFont(); + + ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str()); ImGui::TextUnformatted( - std::vformat(localize.get(FORMAT_PIVOT), std::make_format_args(region.pivot.x, region.pivot.y)) + std::vformat(localize.get(FORMAT_CROP), std::make_format_args(region.crop.x, region.crop.y)) .c_str()); - } - else - { - StringType originString = LABEL_REGION_ORIGIN_CENTER; - if (region.origin == anm2::Spritesheet::Region::TOP_LEFT) originString = LABEL_REGION_ORIGIN_TOP_LEFT; - auto originLabel = localize.get(originString); ImGui::TextUnformatted( - std::vformat(localize.get(FORMAT_ORIGIN), std::make_format_args(originLabel)).c_str()); + std::vformat(localize.get(FORMAT_SIZE), std::make_format_args(region.size.x, region.size.y)) + .c_str()); + if (region.origin == anm2::Spritesheet::Region::CUSTOM) + { + ImGui::TextUnformatted( + std::vformat(localize.get(FORMAT_PIVOT), std::make_format_args(region.pivot.x, region.pivot.y)) + .c_str()); + } + else + { + StringType originString = LABEL_REGION_ORIGIN_CENTER; + if (region.origin == anm2::Spritesheet::Region::TOP_LEFT) + originString = LABEL_REGION_ORIGIN_TOP_LEFT; + auto originLabel = localize.get(originString); + ImGui::TextUnformatted( + std::vformat(localize.get(FORMAT_ORIGIN), std::make_format_args(originLabel)).c_str()); + } } + ImGui::EndChild(); + ImGui::EndTooltip(); } - ImGui::EndChild(); - ImGui::EndTooltip(); } ImGui::PopStyleVar(2); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing); - if (ImGui::BeginDragDropSource()) + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip)) { - static std::vector dragDropSelection{}; - dragDropSelection.assign(selection.begin(), selection.end()); + isRegionDragDropSourceSubmitted = true; + if (selection.contains(id)) + dragDropSelection.assign(selection.begin(), selection.end()); + else + dragDropSelection = {id}; + ImGui::SetDragDropPayload("Region Drag Drop", dragDropSelection.data(), dragDropSelection.size() * sizeof(int)); - - for (auto regionId : dragDropSelection) - { - auto dragIt = spritesheet->regions.find(regionId); - if (dragIt == spritesheet->regions.end()) continue; - ImGui::TextUnformatted(dragIt->second.name.c_str()); - } ImGui::EndDragDropSource(); } - ImGui::PopStyleVar(2); - if (ImGui::BeginDragDropTarget()) { if (auto payload = ImGui::AcceptDragDropPayload("Region Drag Drop")) @@ -450,6 +449,36 @@ namespace anm2ed::imgui ImGui::PopID(); } + auto regionDragPayload = ImGui::GetDragDropPayload(); + bool isRegionDragActive = regionDragPayload && regionDragPayload->IsDataType("Region Drag Drop"); + if (isRegionDragActive && !isRegionDragDropSourceSubmitted && !dragDropSelection.empty() && + ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceExtern | ImGuiDragDropFlags_SourceNoPreviewTooltip)) + { + ImGui::SetDragDropPayload("Region Drag Drop", dragDropSelection.data(), + dragDropSelection.size() * sizeof(int)); + ImGui::EndDragDropSource(); + } + + if (isRegionDragActive && !dragDropSelection.empty()) + { + auto mousePos = ImGui::GetIO().MousePos; + auto tooltipOffset = ImVec2(16.0f, 16.0f); + ImGui::SetNextWindowPos(ImVec2(mousePos.x + tooltipOffset.x, mousePos.y + tooltipOffset.y)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing); + if (ImGui::BeginTooltip()) + { + for (auto regionId : dragDropSelection) + { + auto dragIt = spritesheet->regions.find(regionId); + if (dragIt == spritesheet->regions.end()) continue; + ImGui::TextUnformatted(dragIt->second.name.c_str()); + } + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); + } + ImGui::PopStyleVar(); selection.finish(); diff --git a/src/imgui/window/timeline.cpp b/src/imgui/window/timeline.cpp index 6614ab1..801c510 100644 --- a/src/imgui/window/timeline.cpp +++ b/src/imgui/window/timeline.cpp @@ -1174,6 +1174,10 @@ namespace anm2ed::imgui } }; + float playheadLineCenterX{}; + float playheadLineTopY{}; + bool isPlayheadLineSet{}; + auto frame_child = [&](anm2::Type type, int id, int& index, float width) { auto item = animation ? animation->item_get(type, id) : nullptr; @@ -1307,6 +1311,11 @@ namespace anm2ed::imgui { ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * floorf(playback.time), cursorPos.y)); ImGui::Image(resources.icons[icon::PLAYHEAD].id, frameSize); + auto playheadMin = ImGui::GetItemRectMin(); + auto playheadMax = ImGui::GetItemRectMax(); + playheadLineCenterX = (playheadMin.x + playheadMax.x) * 0.5f; + playheadLineTopY = playheadMax.y; + isPlayheadLineSet = true; overlay_icon(resources.icons[icon::PLAYHEAD].id, playheadIconTint, true); } } @@ -1686,7 +1695,9 @@ namespace anm2ed::imgui ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2()); if (ImGui::BeginChild("##Frames List Child", viewListChildSize, true, ImGuiWindowFlags_HorizontalScrollbar)) { - auto cursorScreenPos = ImGui::GetCursorScreenPos(); + playheadLineCenterX = 0.0f; + playheadLineTopY = 0.0f; + isPlayheadLineSet = false; ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2()); if (ImGui::BeginTable("##Frames List Table", 1, @@ -1757,20 +1768,17 @@ namespace anm2ed::imgui ImDrawList* windowDrawList = ImGui::GetWindowDrawList(); - auto frameSize = ImVec2(ImGui::GetTextLineHeight(), - ImGui::GetTextLineHeightWithSpacing() + (ImGui::GetStyle().WindowPadding.y * 2)); - auto playheadIndex = std::floor(playback.time); - auto lineCenterX = cursorScreenPos.x + frameSize.x * (playheadIndex + 0.6f) - scroll.x; - auto linePos = - ImVec2(lineCenterX - (PLAYHEAD_LINE_THICKNESS * 0.5f), cursorScreenPos.y + (frameSize.y * 2.0f)); - auto lineSize = ImVec2((PLAYHEAD_LINE_THICKNESS / 2.0f), - viewListChildSize.y - frameSize.y - - (isHorizontalScroll ? ImGui::GetStyle().ScrollbarSize * 2.0f : 0.0f)); + auto lineBottomY = ImGui::GetWindowPos().y + ImGui::GetWindowSize().y; + if (isHorizontalScroll) lineBottomY -= ImGui::GetStyle().ScrollbarSize; auto rectMin = windowDrawList->GetClipRectMin(); auto rectMax = windowDrawList->GetClipRectMax(); - if (pickerLineDrawList) + 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)); pickerLineDrawList->PushClipRect(rectMin, rectMax); pickerLineDrawList->AddRectFilled(linePos, ImVec2(linePos.x + lineSize.x, linePos.y + lineSize.y), ImGui::GetColorU32(playheadLineColor)); diff --git a/src/imgui/wizard/render_animation.cpp b/src/imgui/wizard/render_animation.cpp index e4b8da0..9bb70d5 100644 --- a/src/imgui/wizard/render_animation.cpp +++ b/src/imgui/wizard/render_animation.cpp @@ -181,7 +181,7 @@ namespace anm2ed::imgui::wizard ImGui::BeginDisabled(!isRaw); { - input_float_range(localize.get(BASIC_SCALE), scale, 0.1f, 100.0f, 0.1f, STEP_FAST, "%.1fx"); + input_float_range(localize.get(BASIC_SCALE), scale, 0.1f, 100.0f, 1.0f, STEP_FAST, "%.1fx"); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SCALE_OUTPUT)); } ImGui::EndDisabled(); diff --git a/src/manager.cpp b/src/manager.cpp index 466e5c6..f16d31c 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -30,6 +30,15 @@ namespace anm2ed std::filesystem::create_directories(parent, ec); if (ec) logger.warning(std::format("Could not create directory for {}: {}", path::to_utf8(path), ec.message())); } + + void autosave_file_remove(const std::filesystem::path& path) + { + if (path.empty()) return; + + std::error_code ec{}; + std::filesystem::remove(path, ec); + if (ec) logger.warning(std::format("Could not remove autosave file {}: {}", path::to_utf8(path), ec.message())); + } } void Manager::selection_history_push(int index) @@ -107,10 +116,22 @@ namespace anm2ed { std::string errorString{}; ensure_parent_directory_exists(path); + const auto previousAutosavePath = document->autosave_path_get(); document->path = !path.empty() ? path : document->path; document->path.replace_extension(".anm2"); - document->save(document->path, &errorString, compatibility, isBakeSpecialInterpolatedFramesOnSave, isRoundScale, - isRoundRotation); + const auto autosavePath = document->autosave_path_get(); + if (document->save(document->path, &errorString, compatibility, isBakeSpecialInterpolatedFramesOnSave, + isRoundScale, isRoundRotation)) + { + autosaveFiles.erase(std::remove(autosaveFiles.begin(), autosaveFiles.end(), previousAutosavePath), + autosaveFiles.end()); + if (autosavePath != previousAutosavePath) + autosaveFiles.erase(std::remove(autosaveFiles.begin(), autosaveFiles.end(), autosavePath), + autosaveFiles.end()); + autosave_file_remove(previousAutosavePath); + autosave_file_remove(autosavePath); + autosave_files_write(); + } recent_file_add(document->path); } } @@ -142,15 +163,6 @@ namespace anm2ed const auto autosavePath = documents[index].autosave_path_get(); autosaveFiles.erase(std::remove(autosaveFiles.begin(), autosaveFiles.end(), autosavePath), autosaveFiles.end()); - - if (!autosavePath.empty()) - { - std::error_code ec{}; - std::filesystem::remove(autosavePath, ec); - if (ec) - logger.warning(std::format("Could not remove autosave file {}: {}", path::to_utf8(autosavePath), ec.message())); - } - autosave_files_write(); documents.erase(documents.begin() + index); @@ -351,6 +363,8 @@ namespace anm2ed void Manager::autosave_files_open() { + std::vector restoredFiles{}; + for (auto& path : autosaveFiles) { if (auto document = open(path, false, false)) @@ -358,10 +372,16 @@ namespace anm2ed document->isForceDirty = true; document->path = document->path_from_autosave_get(path); document->change(Document::ALL); + restoredFiles.emplace_back(path); } } - autosave_files_clear(); + for (auto& path : restoredFiles) + { + autosaveFiles.erase(std::remove(autosaveFiles.begin(), autosaveFiles.end(), path), autosaveFiles.end()); + autosave_file_remove(path); + } + autosave_files_write(); } void Manager::autosave_files_load() @@ -401,14 +421,11 @@ namespace anm2ed std::format("Could not write autosave files to: {}. Skipping...", path::to_utf8(autosave_path_get()))); } - void Manager::autosave_files_clear() + void Manager::autosave_files_clear(bool removeFiles) { - for (auto& path : autosaveFiles) - { - std::error_code ec{}; - std::filesystem::remove(path, ec); - if (ec) logger.warning(std::format("Could not remove autosave file {}: {}", path::to_utf8(path), ec.message())); - } + if (removeFiles) + for (auto& path : autosaveFiles) + autosave_file_remove(path); autosaveFiles.clear(); autosave_files_write(); @@ -420,5 +437,5 @@ namespace anm2ed chords[i] = imgui::string_to_chord(settings.*SHORTCUT_MEMBERS[i]); } - Manager::~Manager() { autosave_files_clear(); } + Manager::~Manager() = default; } diff --git a/src/manager.hpp b/src/manager.hpp index 3cded74..434e36c 100644 --- a/src/manager.hpp +++ b/src/manager.hpp @@ -95,7 +95,7 @@ namespace anm2ed void autosave_files_load(); void autosave_files_open(); void autosave_files_write(); - void autosave_files_clear(); + void autosave_files_clear(bool removeFiles = false); void chords_set(Settings&); };