Mega Region Update.
This commit is contained in:
@@ -565,6 +565,7 @@ namespace anm2ed::imgui
|
||||
auto crop = frame.crop;
|
||||
auto size = frame.size;
|
||||
auto pivot = frame.pivot;
|
||||
|
||||
if (frame.regionID != -1)
|
||||
{
|
||||
auto regionIt = spritesheet->regions.find(frame.regionID);
|
||||
@@ -576,8 +577,8 @@ namespace anm2ed::imgui
|
||||
}
|
||||
}
|
||||
|
||||
auto layerModel = math::quad_model_get(size, frame.position, pivot,
|
||||
math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto layerModel =
|
||||
math::quad_model_get(size, frame.position, pivot, math::percent_to_unit(frame.scale), frame.rotation);
|
||||
auto layerTransform = sampleTransform * layerModel;
|
||||
|
||||
auto uvMin = crop / texSize;
|
||||
@@ -931,6 +932,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::TextUnformatted(localize.get(TEXT_RECORDING_PROGRESS));
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
||||
{
|
||||
renderFrames.clear();
|
||||
|
||||
@@ -187,7 +187,15 @@ namespace anm2ed::imgui
|
||||
std::set<int> indices{};
|
||||
std::string errorString{};
|
||||
if (anm2.animations_deserialize(clipboardText, start, indices, &errorString))
|
||||
selection = indices;
|
||||
{
|
||||
if (!indices.empty())
|
||||
{
|
||||
auto index = *indices.rbegin();
|
||||
selection = {index};
|
||||
reference = {index};
|
||||
newAnimationSelectedIndex = index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(
|
||||
|
||||
@@ -78,10 +78,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxEventIdBefore = anm2.content.events.empty() ? -1 : anm2.content.events.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_EVENTS));
|
||||
if (anm2.events_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!anm2.content.events.empty())
|
||||
{
|
||||
auto maxEventIdAfter = anm2.content.events.rbegin()->first;
|
||||
if (maxEventIdAfter > maxEventIdBefore)
|
||||
{
|
||||
newEventId = maxEventIdAfter;
|
||||
selection = {maxEventIdAfter};
|
||||
reference = maxEventIdAfter;
|
||||
}
|
||||
}
|
||||
document.change(Document::EVENTS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_EVENTS_FAILED), std::make_format_args(errorString)));
|
||||
|
||||
@@ -53,10 +53,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxLayerIdBefore = anm2.content.layers.empty() ? -1 : anm2.content.layers.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_LAYERS));
|
||||
if (anm2.layers_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!anm2.content.layers.empty())
|
||||
{
|
||||
auto maxLayerIdAfter = anm2.content.layers.rbegin()->first;
|
||||
if (maxLayerIdAfter > maxLayerIdBefore)
|
||||
{
|
||||
newLayerId = maxLayerIdAfter;
|
||||
selection = {maxLayerIdAfter};
|
||||
reference = maxLayerIdAfter;
|
||||
}
|
||||
}
|
||||
document.change(Document::NULLS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_LAYERS_FAILED), std::make_format_args(errorString)));
|
||||
@@ -180,6 +193,7 @@ namespace anm2ed::imgui
|
||||
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
if (ImGui::Button(reference == -1 ? localize.get(BASIC_ADD) : localize.get(BASIC_CONFIRM), widgetSize))
|
||||
{
|
||||
if (reference == -1)
|
||||
@@ -210,6 +224,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) manager.layer_properties_close();
|
||||
|
||||
manager.layer_properties_end();
|
||||
|
||||
@@ -52,10 +52,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxNullIdBefore = anm2.content.nulls.empty() ? -1 : anm2.content.nulls.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_NULLS));
|
||||
if (anm2.nulls_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!anm2.content.nulls.empty())
|
||||
{
|
||||
auto maxNullIdAfter = anm2.content.nulls.rbegin()->first;
|
||||
if (maxNullIdAfter > maxNullIdBefore)
|
||||
{
|
||||
newNullId = maxNullIdAfter;
|
||||
selection = {maxNullIdAfter};
|
||||
reference = maxNullIdAfter;
|
||||
}
|
||||
}
|
||||
document.change(Document::NULLS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_NULLS_FAILED), std::make_format_args(errorString)));
|
||||
@@ -177,6 +190,7 @@ namespace anm2ed::imgui
|
||||
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
if (ImGui::Button(reference == -1 ? localize.get(BASIC_ADD) : localize.get(BASIC_CONFIRM), widgetSize))
|
||||
{
|
||||
if (reference == -1)
|
||||
@@ -207,6 +221,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) manager.null_properties_close();
|
||||
|
||||
ImGui::EndPopup();
|
||||
|
||||
+167
-13
@@ -1,5 +1,6 @@
|
||||
#include "regions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#include <filesystem>
|
||||
@@ -12,6 +13,7 @@
|
||||
#include "path_.h"
|
||||
#include "strings.h"
|
||||
#include "toast.h"
|
||||
#include "vector_.h"
|
||||
|
||||
#include "../../util/map_.h"
|
||||
|
||||
@@ -51,12 +53,32 @@ namespace anm2ed::imgui
|
||||
if (frame.regionID == id) frame.regionID = -1;
|
||||
|
||||
spritesheet->regions.erase(id);
|
||||
auto& order = spritesheet->regionOrder;
|
||||
order.erase(std::remove(order.begin(), order.end(), id), order.end());
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_REGIONS), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto trim = [&]()
|
||||
{
|
||||
if (!spritesheet || selection.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
if (anm2.regions_trim(spritesheetReference, selection))
|
||||
{
|
||||
if (reference != -1 && !selection.contains(reference)) reference = *selection.begin();
|
||||
document.reference = {document.reference.animationIndex};
|
||||
frame.reference = -1;
|
||||
frame.selection.clear();
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_TRIM_REGIONS), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!spritesheet || selection.empty()) return;
|
||||
@@ -73,10 +95,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxRegionIdBefore = spritesheet->regions.empty() ? -1 : spritesheet->regions.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_REGIONS));
|
||||
if (spritesheet->regions_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!spritesheet->regions.empty())
|
||||
{
|
||||
auto maxRegionIdAfter = spritesheet->regions.rbegin()->first;
|
||||
if (maxRegionIdAfter > maxRegionIdBefore)
|
||||
{
|
||||
newRegionId = maxRegionIdAfter;
|
||||
selection = {maxRegionIdAfter};
|
||||
reference = maxRegionIdAfter;
|
||||
}
|
||||
}
|
||||
document.change(Document::SPRITESHEETS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_REGIONS_FAILED), std::make_format_args(errorString)));
|
||||
@@ -127,6 +162,8 @@ namespace anm2ed::imgui
|
||||
properties_open(*selection.begin());
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str())) remove_unused();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_TRIM), nullptr, false, !selection.empty())) trim();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRIM_REGIONS));
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -153,17 +190,51 @@ namespace anm2ed::imgui
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
if (ImGui::BeginChild("##Regions Child", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
auto regionChildSize = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() * 4);
|
||||
auto regionChildSize = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() * 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
|
||||
selection.start(spritesheet->regions.size());
|
||||
auto rebuild_order = [&]()
|
||||
{
|
||||
spritesheet->regionOrder.clear();
|
||||
spritesheet->regionOrder.reserve(spritesheet->regions.size());
|
||||
for (auto id : spritesheet->regions | std::views::keys)
|
||||
spritesheet->regionOrder.push_back(id);
|
||||
};
|
||||
if (spritesheet->regionOrder.size() != spritesheet->regions.size())
|
||||
rebuild_order();
|
||||
else
|
||||
{
|
||||
bool isOrderValid = true;
|
||||
for (auto id : spritesheet->regionOrder)
|
||||
if (!spritesheet->regions.contains(id))
|
||||
{
|
||||
isOrderValid = false;
|
||||
break;
|
||||
}
|
||||
if (!isOrderValid) rebuild_order();
|
||||
}
|
||||
|
||||
selection.set_index_map(&spritesheet->regionOrder);
|
||||
selection.start(spritesheet->regionOrder.size());
|
||||
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, ImGuiInputFlags_RouteFocused))
|
||||
{
|
||||
selection.clear();
|
||||
for (auto& id : spritesheet->regionOrder)
|
||||
selection.insert(id);
|
||||
}
|
||||
if (ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteFocused)) selection.clear();
|
||||
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);
|
||||
|
||||
for (auto& [id, region] : spritesheet->regions)
|
||||
for (int i = 0; i < (int)spritesheet->regionOrder.size(); i++)
|
||||
{
|
||||
int id = spritesheet->regionOrder[i];
|
||||
auto regionIt = spritesheet->regions.find(id);
|
||||
if (regionIt == spritesheet->regions.end()) continue;
|
||||
auto& region = regionIt->second;
|
||||
auto isNewRegion = newRegionId == id;
|
||||
auto nameCStr = region.name.c_str();
|
||||
auto isSelected = selection.contains(id);
|
||||
auto isReferenced = id == reference;
|
||||
@@ -174,7 +245,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
|
||||
ImGui::SetNextItemSelectionUserData(id);
|
||||
ImGui::SetNextItemSelectionUserData(i);
|
||||
ImGui::SetNextItemStorageID(id);
|
||||
if (ImGui::Selectable("##Region Selectable", isSelected, 0, regionChildSize))
|
||||
{
|
||||
@@ -194,8 +265,13 @@ namespace anm2ed::imgui
|
||||
auto scale = glm::min(maxPreviewSize.x / previewSize.x, maxPreviewSize.y / previewSize.y);
|
||||
previewSize *= scale;
|
||||
}
|
||||
auto uvMin = region.crop / vec2(texture.size);
|
||||
auto uvMax = (region.crop + region.size) / vec2(texture.size);
|
||||
vec2 uvMin{};
|
||||
vec2 uvMax{1.0f, 1.0f};
|
||||
if (isValid)
|
||||
{
|
||||
uvMin = region.crop / vec2(texture.size);
|
||||
uvMax = (region.crop + region.size) / vec2(texture.size);
|
||||
}
|
||||
|
||||
auto textWidth = ImGui::CalcTextSize(nameCStr).x;
|
||||
auto tooltipPadding = style.WindowPadding.x * 4.0f;
|
||||
@@ -233,15 +309,70 @@ namespace anm2ed::imgui
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_SIZE), std::make_format_args(region.size.x, region.size.y))
|
||||
.c_str());
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_PIVOT), std::make_format_args(region.pivot.x, region.pivot.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::PopStyleVar(2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||
if (ImGui::BeginDragDropSource())
|
||||
{
|
||||
static std::vector<int> dragDropSelection{};
|
||||
dragDropSelection.assign(selection.begin(), selection.end());
|
||||
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"))
|
||||
{
|
||||
auto payloadIds = (int*)(payload->Data);
|
||||
int payloadCount = (int)(payload->DataSize / sizeof(int));
|
||||
std::vector<int> indices{};
|
||||
indices.reserve(payloadCount);
|
||||
for (int payloadIndex = 0; payloadIndex < payloadCount; payloadIndex++)
|
||||
{
|
||||
int payloadId = payloadIds[payloadIndex];
|
||||
int index = vector::find_index(spritesheet->regionOrder, payloadId);
|
||||
if (index != -1) indices.push_back(index);
|
||||
}
|
||||
if (!indices.empty())
|
||||
{
|
||||
std::sort(indices.begin(), indices.end());
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_MOVE_REGIONS), Document::SPRITESHEETS,
|
||||
vector::move_indices(spritesheet->regionOrder, indices, i));
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
auto imageSize = to_imvec2(vec2(regionChildSize.y));
|
||||
auto aspectRatio = region.size.y != 0.0f ? (float)region.size.x / region.size.y : 1.0f;
|
||||
|
||||
@@ -257,12 +388,18 @@ namespace anm2ed::imgui
|
||||
regionChildSize.y - regionChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
|
||||
|
||||
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, nameCStr)).c_str());
|
||||
ImGui::TextUnformatted(nameCStr);
|
||||
if (isReferenced) ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (isNewRegion)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newRegionId = -1;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
@@ -298,28 +435,43 @@ namespace anm2ed::imgui
|
||||
|
||||
if (ImGui::BeginPopupModal(propertiesPopup.label(), &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
auto childSize = child_size_get(4);
|
||||
auto childSize = child_size_get(5);
|
||||
auto& region = reference == -1 ? editRegion : spritesheet->regions.at(reference);
|
||||
|
||||
if (propertiesPopup.isJustOpened) editRegion = anm2::Spritesheet::Region{};
|
||||
|
||||
if (ImGui::BeginChild("##Child", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
const char* originOptions[] = {localize.get(LABEL_REGION_ORIGIN_TOP_LEFT),
|
||||
localize.get(LABEL_REGION_ORIGIN_CENTER),
|
||||
localize.get(LABEL_REGION_ORIGIN_CUSTOM)};
|
||||
|
||||
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
|
||||
input_text_string(localize.get(BASIC_NAME), ®ion.name);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ITEM_NAME));
|
||||
|
||||
ImGui::DragFloat2(localize.get(BASIC_CROP), value_ptr(region.crop), DRAG_SPEED, 0.0f, 0.0f,
|
||||
math::vec2_format_get(region.crop));
|
||||
ImGui::DragFloat2(localize.get(BASIC_SIZE), value_ptr(region.size), DRAG_SPEED, 0.0f, 0.0f,
|
||||
math::vec2_format_get(region.size));
|
||||
ImGui::BeginDisabled(region.origin != anm2::Spritesheet::Region::CUSTOM);
|
||||
ImGui::DragFloat2(localize.get(BASIC_PIVOT), value_ptr(region.pivot), DRAG_SPEED, 0.0f, 0.0f,
|
||||
math::vec2_format_get(region.pivot));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::Combo(localize.get(LABEL_REGION_PROPERTIES_ORIGIN), (int*)®ion.origin, originOptions,
|
||||
IM_ARRAYSIZE(originOptions)))
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REGION_PROPERTIES_ORIGIN));
|
||||
|
||||
if (region.origin == anm2::Spritesheet::Region::TOP_LEFT)
|
||||
region.pivot = {};
|
||||
else if (region.origin == anm2::Spritesheet::Region::ORIGIN_CENTER)
|
||||
region.pivot = {(int)(region.size.x / 2.0f), (int)(region.size.y / 2.0f)};
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
if (ImGui::Button(reference == -1 ? localize.get(BASIC_ADD) : localize.get(BASIC_CONFIRM), widgetSize))
|
||||
{
|
||||
if (reference == -1)
|
||||
@@ -328,6 +480,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto id = map::next_id_get(spritesheet->regions);
|
||||
spritesheet->regions[id] = region;
|
||||
spritesheet->regionOrder.push_back(id);
|
||||
selection = {id};
|
||||
newRegionId = id;
|
||||
};
|
||||
@@ -353,6 +506,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) propertiesPopup.close();
|
||||
|
||||
ImGui::EndPopup();
|
||||
|
||||
@@ -130,10 +130,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxSoundIdBefore = anm2.content.sounds.empty() ? -1 : anm2.content.sounds.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(TOAST_SOUNDS_PASTE));
|
||||
if (anm2.sounds_deserialize(clipboard.get(), document.directory_get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!anm2.content.sounds.empty())
|
||||
{
|
||||
auto maxSoundIdAfter = anm2.content.sounds.rbegin()->first;
|
||||
if (maxSoundIdAfter > maxSoundIdBefore)
|
||||
{
|
||||
newSoundId = maxSoundIdAfter;
|
||||
selection = {maxSoundIdAfter};
|
||||
reference = maxSoundIdAfter;
|
||||
}
|
||||
}
|
||||
document.change(Document::SOUNDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUNDS_DESERIALIZE_ERROR), std::make_format_args(errorString)));
|
||||
@@ -200,9 +213,17 @@ namespace anm2ed::imgui
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
|
||||
selection.start(anm2.content.sounds.size());
|
||||
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, ImGuiInputFlags_RouteFocused))
|
||||
{
|
||||
selection.clear();
|
||||
for (auto& id : anm2.content.sounds | std::views::keys)
|
||||
selection.insert(id);
|
||||
}
|
||||
if (ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteFocused)) selection.clear();
|
||||
|
||||
for (auto& [id, sound] : anm2.content.sounds)
|
||||
{
|
||||
auto isNewSound = newSoundId == id;
|
||||
ImGui::PushID(id);
|
||||
|
||||
if (ImGui::BeginChild("##Sound Child", soundChildSize, ImGuiChildFlags_Borders))
|
||||
@@ -222,11 +243,6 @@ namespace anm2ed::imgui
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) play(sound);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) open_directory(sound);
|
||||
if (newSoundId == id)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSoundId = -1;
|
||||
}
|
||||
|
||||
auto textWidth = ImGui::CalcTextSize(pathString.c_str()).x;
|
||||
auto tooltipPadding = style.WindowPadding.x * 4.0f;
|
||||
@@ -267,6 +283,13 @@ namespace anm2ed::imgui
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (isNewSound)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSoundId = -1;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace anm2ed::imgui
|
||||
auto& tool = settings.tool;
|
||||
auto& shaderGrid = resources.shaders[shader::GRID];
|
||||
auto& shaderTexture = resources.shaders[shader::TEXTURE];
|
||||
auto& shaderLine = resources.shaders[shader::LINE];
|
||||
auto& dashedShader = resources.shaders[shader::DASHED];
|
||||
auto& frames = document.frames.selection;
|
||||
auto& regionReference = document.region.reference;
|
||||
@@ -178,11 +179,18 @@ namespace anm2ed::imgui
|
||||
ImGui::EndChild();
|
||||
|
||||
auto drawList = ImGui::GetCurrentWindow()->DrawList;
|
||||
size_set(to_vec2(ImGui::GetContentRegionAvail()));
|
||||
|
||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||
auto min = ImGui::GetCursorScreenPos();
|
||||
auto max = to_imvec2(to_vec2(min) + size);
|
||||
|
||||
size_set(to_vec2(ImGui::GetContentRegionAvail()));
|
||||
auto mouseScreenPos = ImGui::GetIO().MousePos;
|
||||
bool isMouseOverCanvas = mouseScreenPos.x >= min.x && mouseScreenPos.x <= max.x && mouseScreenPos.y >= min.y &&
|
||||
mouseScreenPos.y <= max.y;
|
||||
auto hoverMousePos = vec2();
|
||||
if (isMouseOverCanvas)
|
||||
hoverMousePos = position_translate(zoom, pan, to_ivec2(mouseScreenPos) - to_ivec2(cursorScreenPos));
|
||||
|
||||
bind();
|
||||
viewport_set();
|
||||
@@ -207,32 +215,73 @@ namespace anm2ed::imgui
|
||||
rect_render(dashedShader, spritesheetTransform, spritesheetModel, color::WHITE, BORDER_DASH_LENGTH,
|
||||
BORDER_DASH_GAP, BORDER_DASH_OFFSET);
|
||||
|
||||
if (frame && reference.itemID > -1 &&
|
||||
anm2.content.layers.at(reference.itemID).spritesheetID == referenceSpritesheet)
|
||||
if (hoveredRegionId != -1)
|
||||
{
|
||||
auto cropModel = math::quad_model_get(frame->size, frame->crop);
|
||||
auto cropTransform = transform * cropModel;
|
||||
rect_render(dashedShader, cropTransform, cropModel, color::RED);
|
||||
|
||||
auto pivotTransform =
|
||||
transform * math::quad_model_get(PIVOT_SIZE, frame->crop + frame->pivot, PIVOT_SIZE * 0.5f);
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, PIVOT_COLOR);
|
||||
}
|
||||
else if (regionReference != -1)
|
||||
{
|
||||
auto regionIt = spritesheet->regions.find(regionReference);
|
||||
auto regionIt = spritesheet->regions.find(hoveredRegionId);
|
||||
if (regionIt != spritesheet->regions.end())
|
||||
{
|
||||
auto& region = regionIt->second;
|
||||
auto cropModel = math::quad_model_get(region.size, region.crop);
|
||||
auto cropTransform = transform * cropModel;
|
||||
rect_render(dashedShader, cropTransform, cropModel, color::RED);
|
||||
rect_fill_render(shaderLine, cropTransform, cropModel, vec4(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
int highlightedRegionId = -1;
|
||||
if (frame && reference.itemID > -1 &&
|
||||
anm2.content.layers.at(reference.itemID).spritesheetID == referenceSpritesheet && frame->regionID != -1 &&
|
||||
spritesheet->regions.contains(frame->regionID))
|
||||
{
|
||||
highlightedRegionId = frame->regionID;
|
||||
}
|
||||
else if (regionReference != -1 && spritesheet->regions.contains(regionReference))
|
||||
{
|
||||
highlightedRegionId = regionReference;
|
||||
}
|
||||
|
||||
auto draw_region_rect = [&](anm2::Spritesheet::Region& region, vec4 regionColor)
|
||||
{
|
||||
auto cropModel = math::quad_model_get(region.size, region.crop);
|
||||
auto cropTransform = transform * cropModel;
|
||||
rect_render(dashedShader, cropTransform, cropModel, regionColor, BORDER_DASH_LENGTH, BORDER_DASH_GAP,
|
||||
BORDER_DASH_OFFSET);
|
||||
};
|
||||
|
||||
for (auto& [id, region] : spritesheet->regions)
|
||||
{
|
||||
if (id == highlightedRegionId) continue;
|
||||
draw_region_rect(region, color::WHITE);
|
||||
|
||||
auto pivotTransform =
|
||||
transform * math::quad_model_get(PIVOT_SIZE, region.crop + region.pivot, PIVOT_SIZE * 0.5f);
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color::WHITE);
|
||||
}
|
||||
|
||||
if (highlightedRegionId != -1)
|
||||
{
|
||||
auto regionIt = spritesheet->regions.find(highlightedRegionId);
|
||||
if (regionIt != spritesheet->regions.end())
|
||||
{
|
||||
auto& region = regionIt->second;
|
||||
draw_region_rect(region, color::RED);
|
||||
|
||||
auto pivotTransform =
|
||||
transform * math::quad_model_get(PIVOT_SIZE, region.crop + region.pivot, PIVOT_SIZE * 0.5f);
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, PIVOT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
bool isFrameOnSpritesheet =
|
||||
frame && reference.itemID > -1 && anm2.content.layers.at(reference.itemID).spritesheetID == referenceSpritesheet;
|
||||
if (isFrameOnSpritesheet && frame->regionID == -1)
|
||||
{
|
||||
auto frameModel = math::quad_model_get(frame->size, frame->crop);
|
||||
auto frameTransform = transform * frameModel;
|
||||
rect_render(shaderLine, frameTransform, frameModel, color::RED);
|
||||
|
||||
auto pivotTransform = transform * math::quad_model_get(PIVOT_SIZE, frame->crop + frame->pivot, PIVOT_SIZE * 0.5f);
|
||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, PIVOT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
unbind();
|
||||
@@ -320,6 +369,8 @@ namespace anm2ed::imgui
|
||||
|
||||
auto frame_change_apply = [&](anm2::FrameChange frameChange, anm2::ChangeType changeType = anm2::ADJUST)
|
||||
{ item->frames_change(frameChange, reference.itemType, changeType, frames); };
|
||||
auto clamp_vec2_to_int = [](const vec2& value)
|
||||
{ return vec2(ivec2(value)); };
|
||||
auto region_set_all = [&](const vec2& crop, const vec2& size)
|
||||
{
|
||||
if (!spritesheet) return;
|
||||
@@ -327,8 +378,8 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto it = spritesheet->regions.find(id);
|
||||
if (it == spritesheet->regions.end()) continue;
|
||||
it->second.crop = crop;
|
||||
it->second.size = size;
|
||||
it->second.crop = clamp_vec2_to_int(crop);
|
||||
it->second.size = clamp_vec2_to_int(size);
|
||||
}
|
||||
};
|
||||
auto region_offset_all = [&](const vec2& delta)
|
||||
@@ -338,7 +389,8 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto it = spritesheet->regions.find(id);
|
||||
if (it == spritesheet->regions.end()) continue;
|
||||
it->second.crop += delta;
|
||||
it->second.crop = clamp_vec2_to_int(it->second.crop + delta);
|
||||
it->second.size = clamp_vec2_to_int(it->second.size);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -348,18 +400,32 @@ namespace anm2ed::imgui
|
||||
if (tool == tool::DRAW && isMouseRightDown) useTool = tool::ERASE;
|
||||
if (tool == tool::ERASE && isMouseRightDown) useTool = tool::DRAW;
|
||||
|
||||
hoveredRegionId = -1;
|
||||
|
||||
if (useTool == tool::PAN && spritesheet && spritesheet->texture.is_valid() && isMouseOverCanvas)
|
||||
{
|
||||
for (auto& [id, region] : spritesheet->regions)
|
||||
{
|
||||
auto minPoint = glm::min(region.crop, region.crop + region.size);
|
||||
auto maxPoint = glm::max(region.crop, region.crop + region.size);
|
||||
if (hoverMousePos.x >= minPoint.x && hoverMousePos.x <= maxPoint.x && hoverMousePos.y >= minPoint.y &&
|
||||
hoverMousePos.y <= maxPoint.y)
|
||||
{
|
||||
hoveredRegionId = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& toolInfo = tool::INFO[useTool];
|
||||
auto& areaType = toolInfo.areaType;
|
||||
bool isAreaAllowed = areaType == tool::ALL || areaType == tool::SPRITESHEET_EDITOR;
|
||||
bool isFrameRequired =
|
||||
!(useTool == tool::PAN || useTool == tool::DRAW || useTool == tool::ERASE || useTool == tool::COLOR_PICKER);
|
||||
bool isRegionInUse =
|
||||
frame && frame->regionID != -1 && (useTool == tool::CROP || useTool == tool::MOVE);
|
||||
bool isFrameAvailable =
|
||||
!isFrameRequired ||
|
||||
(frame && !isRegionInUse) ||
|
||||
(useTool == tool::CROP && !frame && !regionSelection.empty()) ||
|
||||
(useTool == tool::MOVE && !frame && regionReference != -1);
|
||||
bool isRegionInUse = frame && frame->regionID != -1 && (useTool == tool::CROP || useTool == tool::MOVE);
|
||||
bool isFrameAvailable = !isFrameRequired || (frame && !isRegionInUse) ||
|
||||
(useTool == tool::CROP && !frame && !regionSelection.empty()) ||
|
||||
(useTool == tool::MOVE && !frame && regionReference != -1);
|
||||
bool isSpritesheetRequired = useTool == tool::DRAW || useTool == tool::ERASE || useTool == tool::COLOR_PICKER;
|
||||
bool isSpritesheetAvailable = !isSpritesheetRequired || (spritesheet && spritesheet->texture.is_valid());
|
||||
auto cursor = (isAreaAllowed && isFrameAvailable && isSpritesheetAvailable) ? toolInfo.cursor
|
||||
@@ -370,6 +436,29 @@ namespace anm2ed::imgui
|
||||
switch (useTool)
|
||||
{
|
||||
case tool::PAN:
|
||||
if (isMouseLeftClicked && hoveredRegionId != -1)
|
||||
{
|
||||
regionReference = hoveredRegionId;
|
||||
regionSelection = {hoveredRegionId};
|
||||
if (frame && reference.itemID > -1 &&
|
||||
anm2.content.layers.at(reference.itemID).spritesheetID == referenceSpritesheet)
|
||||
{
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_REGION), Document::FRAMES,
|
||||
{
|
||||
frame->regionID = hoveredRegionId;
|
||||
if (spritesheet)
|
||||
{
|
||||
auto regionIt = spritesheet->regions.find(hoveredRegionId);
|
||||
if (regionIt != spritesheet->regions.end())
|
||||
{
|
||||
frame->crop = regionIt->second.crop;
|
||||
frame->size = regionIt->second.size;
|
||||
frame->pivot = regionIt->second.pivot;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isMouseDown || isMouseMiddleDown) pan += mouseDelta;
|
||||
break;
|
||||
case tool::MOVE:
|
||||
@@ -382,8 +471,9 @@ namespace anm2ed::imgui
|
||||
|
||||
auto& region = regionIt->second;
|
||||
if (isBegin) document.snapshot(localize.get(EDIT_REGION_MOVE));
|
||||
if (isMouseDown)
|
||||
region.pivot = ivec2(mousePos) - ivec2(region.crop);
|
||||
bool isPivotEdited = isMouseDown || isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
|
||||
if (isPivotEdited) region.origin = anm2::Spritesheet::Region::CUSTOM;
|
||||
if (isMouseDown) region.pivot = ivec2(mousePos) - ivec2(region.crop);
|
||||
if (isLeftPressed) region.pivot.x -= step;
|
||||
if (isRightPressed) region.pivot.x += step;
|
||||
if (isUpPressed) region.pivot.y -= step;
|
||||
@@ -466,14 +556,14 @@ namespace anm2ed::imgui
|
||||
auto& region = it->second;
|
||||
auto minPoint = glm::min(region.crop, region.crop + region.size);
|
||||
auto maxPoint = glm::max(region.crop, region.crop + region.size);
|
||||
region.crop = minPoint;
|
||||
region.size = maxPoint - minPoint;
|
||||
region.crop = clamp_vec2_to_int(minPoint);
|
||||
region.size = clamp_vec2_to_int(maxPoint - minPoint);
|
||||
|
||||
if (isGridSnap)
|
||||
{
|
||||
auto [snapMin, snapMax] = snap_rect(region.crop, region.crop + region.size);
|
||||
region.crop = snapMin;
|
||||
region.size = snapMax - snapMin;
|
||||
region.crop = clamp_vec2_to_int(snapMin);
|
||||
region.size = clamp_vec2_to_int(snapMax - snapMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -483,14 +573,12 @@ namespace anm2ed::imgui
|
||||
auto it = spritesheet->regions.find(*regionSelection.begin());
|
||||
if (it != spritesheet->regions.end())
|
||||
{
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_CROP),
|
||||
std::make_format_args(it->second.crop.x, it->second.crop.y))
|
||||
.c_str());
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_SIZE),
|
||||
std::make_format_args(it->second.size.x, it->second.size.y))
|
||||
.c_str());
|
||||
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_CROP),
|
||||
std::make_format_args(it->second.crop.x, it->second.crop.y))
|
||||
.c_str());
|
||||
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_SIZE),
|
||||
std::make_format_args(it->second.size.x, it->second.size.y))
|
||||
.c_str());
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
@@ -593,6 +681,42 @@ namespace anm2ed::imgui
|
||||
break;
|
||||
}
|
||||
|
||||
if (tool == tool::PAN && hoveredRegionId != -1 && spritesheet)
|
||||
{
|
||||
auto regionIt = spritesheet->regions.find(hoveredRegionId);
|
||||
if (regionIt != spritesheet->regions.end())
|
||||
{
|
||||
if (ImGui::BeginTooltip())
|
||||
{
|
||||
auto& region = regionIt->second;
|
||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||
ImGui::TextUnformatted(region.name.c_str());
|
||||
ImGui::PopFont();
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_ID), std::make_format_args(hoveredRegionId)).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)
|
||||
{
|
||||
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::EndTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((isMouseDown || isKeyDown) && useTool != tool::PAN)
|
||||
{
|
||||
if (!isAreaAllowed && areaType == tool::ANIMATION_PREVIEW)
|
||||
@@ -617,8 +741,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (isRegionInUse)
|
||||
ImGui::TextUnformatted(localize.get(TEXT_REGION_IN_USE));
|
||||
else
|
||||
if (useTool == tool::CROP)
|
||||
else if (useTool == tool::CROP)
|
||||
ImGui::TextUnformatted(localize.get(TEXT_SELECT_FRAME_OR_REGION));
|
||||
else
|
||||
ImGui::TextUnformatted(localize.get(TEXT_SELECT_FRAME));
|
||||
@@ -681,4 +804,5 @@ namespace anm2ed::imgui
|
||||
settings.editorStartZoom = zoom;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace anm2ed::imgui
|
||||
float checkerSyncZoom{};
|
||||
bool isCheckerPanInitialized{};
|
||||
bool hasPendingZoomPanAdjust{};
|
||||
int hoveredRegionId{-1};
|
||||
|
||||
public:
|
||||
SpritesheetEditor();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
|
||||
#include "document.h"
|
||||
#include "log.h"
|
||||
@@ -27,9 +28,24 @@ namespace anm2ed::imgui
|
||||
auto& reference = document.spritesheet.reference;
|
||||
auto& region = document.region;
|
||||
auto style = ImGui::GetStyle();
|
||||
std::function<void()> pack{};
|
||||
|
||||
auto add_open = [&]() { dialog.file_open(Dialog::SPRITESHEET_OPEN); };
|
||||
auto replace_open = [&]() { dialog.file_open(Dialog::SPRITESHEET_REPLACE); };
|
||||
auto merge_open = [&]()
|
||||
{
|
||||
if (selection.size() <= 1) return;
|
||||
mergeSelection = selection;
|
||||
mergePopup.open();
|
||||
};
|
||||
auto pack_open = [&]()
|
||||
{
|
||||
if (selection.size() != 1) return;
|
||||
auto id = *selection.begin();
|
||||
if (!anm2.content.spritesheets.contains(id)) return;
|
||||
if (anm2.content.spritesheets.at(id).regions.empty()) return;
|
||||
if (pack) pack();
|
||||
};
|
||||
|
||||
auto add = [&](const std::filesystem::path& path)
|
||||
{
|
||||
@@ -87,7 +103,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
auto& id = *selection.begin();
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
spritesheet = anm2::Spritesheet(document.directory_get(), path);
|
||||
spritesheet.reload(document.directory_get(), path);
|
||||
auto pathString = path::to_utf8(spritesheet.path);
|
||||
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
@@ -120,6 +136,55 @@ namespace anm2ed::imgui
|
||||
}
|
||||
};
|
||||
|
||||
auto merge = [&]()
|
||||
{
|
||||
if (mergeSelection.size() <= 1) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto baseID = *mergeSelection.begin();
|
||||
if (anm2.spritesheets_merge(mergeSelection, (anm2::SpritesheetMergeOrigin)settings.mergeSpritesheetsOrigin,
|
||||
settings.mergeSpritesheetsIsMakeRegions,
|
||||
(origin::Type)settings.mergeSpritesheetsRegionOrigin))
|
||||
{
|
||||
selection = {baseID};
|
||||
reference = baseID;
|
||||
region.reference = -1;
|
||||
region.selection.clear();
|
||||
toasts.push(localize.get(TOAST_MERGE_SPRITESHEETS));
|
||||
logger.info(localize.get(TOAST_MERGE_SPRITESHEETS, anm2ed::ENGLISH));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(localize.get(TOAST_MERGE_SPRITESHEETS_FAILED));
|
||||
logger.error(localize.get(TOAST_MERGE_SPRITESHEETS_FAILED, anm2ed::ENGLISH));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_MERGE_SPRITESHEETS), Document::ALL, behavior());
|
||||
};
|
||||
pack = [&]()
|
||||
{
|
||||
if (selection.size() != 1) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto id = *selection.begin();
|
||||
if (anm2.spritesheet_pack(id))
|
||||
{
|
||||
toasts.push(localize.get(TOAST_PACK_SPRITESHEET));
|
||||
logger.info(localize.get(TOAST_PACK_SPRITESHEET, anm2ed::ENGLISH));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(localize.get(TOAST_PACK_SPRITESHEET_FAILED));
|
||||
logger.error(localize.get(TOAST_PACK_SPRITESHEET_FAILED, anm2ed::ENGLISH));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PACK_SPRITESHEET), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto open_directory = [&](anm2::Spritesheet& spritesheet)
|
||||
{
|
||||
if (spritesheet.path.empty()) return;
|
||||
@@ -148,10 +213,25 @@ namespace anm2ed::imgui
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto maxSpritesheetIdBefore = anm2.content.spritesheets.empty() ? -1 : anm2.content.spritesheets.rbegin()->first;
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_SPRITESHEETS));
|
||||
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get(), merge::APPEND, &errorString))
|
||||
{
|
||||
if (!anm2.content.spritesheets.empty())
|
||||
{
|
||||
auto maxSpritesheetIdAfter = anm2.content.spritesheets.rbegin()->first;
|
||||
if (maxSpritesheetIdAfter > maxSpritesheetIdBefore)
|
||||
{
|
||||
newSpritesheetId = maxSpritesheetIdAfter;
|
||||
selection = {maxSpritesheetIdAfter};
|
||||
reference = maxSpritesheetIdAfter;
|
||||
region.reference = -1;
|
||||
region.selection.clear();
|
||||
}
|
||||
}
|
||||
document.change(Document::SPRITESHEETS);
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(
|
||||
@@ -191,8 +271,17 @@ namespace anm2ed::imgui
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str())) remove_unused();
|
||||
|
||||
bool isPackable =
|
||||
selection.size() == 1 && anm2.content.spritesheets.contains(*selection.begin()) &&
|
||||
!anm2.content.spritesheets.at(*selection.begin()).regions.empty();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_RELOAD), nullptr, false, !selection.empty())) reload();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE), nullptr, false, selection.size() == 1)) replace_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_MERGE), settings.shortcutMerge.c_str(), false, selection.size() > 1))
|
||||
merge_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PACK), nullptr, false, isPackable)) pack_open();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PACK_SPRITESHEET));
|
||||
if (ImGui::MenuItem(localize.get(BASIC_SAVE), nullptr, false, !selection.empty())) save();
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -217,9 +306,17 @@ namespace anm2ed::imgui
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
|
||||
selection.start(anm2.content.spritesheets.size());
|
||||
if (ImGui::Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, ImGuiInputFlags_RouteFocused))
|
||||
{
|
||||
selection.clear();
|
||||
for (auto& id : anm2.content.spritesheets | std::views::keys)
|
||||
selection.insert(id);
|
||||
}
|
||||
if (ImGui::Shortcut(ImGuiKey_Escape, ImGuiInputFlags_RouteFocused)) selection.clear();
|
||||
|
||||
for (auto& [id, spritesheet] : anm2.content.spritesheets)
|
||||
{
|
||||
auto isNewSpritesheet = newSpritesheetId == id;
|
||||
ImGui::PushID(id);
|
||||
|
||||
if (ImGui::BeginChild("##Spritesheet Child", spritesheetChildSize, ImGuiChildFlags_Borders))
|
||||
@@ -243,11 +340,6 @@ namespace anm2ed::imgui
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
open_directory(spritesheet);
|
||||
if (newSpritesheetId == id)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSpritesheetId = -1;
|
||||
}
|
||||
|
||||
auto viewport = ImGui::GetMainViewport();
|
||||
auto maxPreviewSize = to_vec2(viewport->Size) * 0.5f;
|
||||
@@ -325,6 +417,13 @@ namespace anm2ed::imgui
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (isNewSpritesheet)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSpritesheetId = -1;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
@@ -385,7 +484,64 @@ namespace anm2ed::imgui
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_REMOVE], shortcut::FOCUSED)) remove_unused();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_MERGE], shortcut::FOCUSED) && selection.size() > 1) merge_open();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
mergePopup.trigger();
|
||||
if (ImGui::BeginPopupModal(mergePopup.label(), &mergePopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
settings.mergeSpritesheetsRegionOrigin =
|
||||
glm::clamp(settings.mergeSpritesheetsRegionOrigin, (int)origin::TOP_LEFT, (int)origin::ORIGIN_CENTER);
|
||||
|
||||
auto close = [&]()
|
||||
{
|
||||
mergeSelection.clear();
|
||||
mergePopup.close();
|
||||
};
|
||||
|
||||
auto optionsSize = child_size_get(5);
|
||||
if (ImGui::BeginChild("##Merge Spritesheets Options", optionsSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
ImGui::SeparatorText(localize.get(LABEL_REGION_PROPERTIES_ORIGIN));
|
||||
ImGui::RadioButton(localize.get(LABEL_MERGE_SPRITESHEETS_APPEND_BOTTOM), &settings.mergeSpritesheetsOrigin,
|
||||
anm2::APPEND_BOTTOM);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MERGE_SPRITESHEETS_BOTTOM_LEFT));
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(localize.get(LABEL_MERGE_SPRITESHEETS_APPEND_RIGHT), &settings.mergeSpritesheetsOrigin,
|
||||
anm2::APPEND_RIGHT);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MERGE_SPRITESHEETS_TOP_RIGHT));
|
||||
|
||||
ImGui::SeparatorText(localize.get(LABEL_OPTIONS));
|
||||
ImGui::Checkbox(localize.get(LABEL_MERGE_MAKE_SPRITESHEET_REGIONS), &settings.mergeSpritesheetsIsMakeRegions);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MERGE_MAKE_SPRITESHEET_REGIONS));
|
||||
|
||||
const char* regionOriginOptions[] = {localize.get(LABEL_REGION_ORIGIN_TOP_LEFT),
|
||||
localize.get(LABEL_REGION_ORIGIN_CENTER)};
|
||||
ImGui::BeginDisabled(!settings.mergeSpritesheetsIsMakeRegions);
|
||||
ImGui::Combo(localize.get(LABEL_REGION_PROPERTIES_ORIGIN), &settings.mergeSpritesheetsRegionOrigin,
|
||||
regionOriginOptions, IM_ARRAYSIZE(regionOriginOptions));
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
ImGui::BeginDisabled(mergeSelection.size() <= 1);
|
||||
if (ImGui::Button(localize.get(BASIC_MERGE), widgetSize))
|
||||
{
|
||||
merge();
|
||||
close();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) close();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
mergePopup.end();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace anm2ed::imgui
|
||||
class Spritesheets
|
||||
{
|
||||
int newSpritesheetId{-1};
|
||||
PopupHelper mergePopup{PopupHelper(LABEL_SPRITESHEETS_MERGE_POPUP, imgui::POPUP_SMALL_NO_HEIGHT)};
|
||||
std::set<int> mergeSelection{};
|
||||
|
||||
public:
|
||||
void update(Manager&, Settings&, Resources&, Dialog&, Clipboard& clipboard);
|
||||
|
||||
@@ -395,17 +395,19 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (reference.itemType == anm2::LAYER && reference.itemID != -1)
|
||||
{
|
||||
auto& layer = anm2.content.layers.at(reference.itemID);
|
||||
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
||||
if (spritesheet)
|
||||
anm2::Spritesheet* spritesheet = nullptr;
|
||||
if (anm2.content.layers.contains(reference.itemID))
|
||||
{
|
||||
for (auto i : indices)
|
||||
{
|
||||
if (!vector::in_bounds(item->frames, i)) continue;
|
||||
auto& frame = item->frames[i];
|
||||
if (frame.regionID != -1 && !spritesheet->regions.contains(frame.regionID))
|
||||
frame.regionID = -1;
|
||||
}
|
||||
auto& layer = anm2.content.layers.at(reference.itemID);
|
||||
spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
||||
}
|
||||
|
||||
for (auto i : indices)
|
||||
{
|
||||
if (!vector::in_bounds(item->frames, i)) continue;
|
||||
auto& frame = item->frames[i];
|
||||
if (frame.regionID == -1) continue;
|
||||
if (!spritesheet || !spritesheet->regions.contains(frame.regionID)) frame.regionID = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1774,6 +1776,7 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
ImGui::BeginDisabled(source == source::EXISTING && addItemID == -1);
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
|
||||
{
|
||||
anm2::Reference addReference{};
|
||||
@@ -1797,6 +1800,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) item_properties_close();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CANCEL_ADD_ITEM));
|
||||
|
||||
@@ -1825,6 +1829,7 @@ namespace anm2ed::imgui
|
||||
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CONFIRM]);
|
||||
if (ImGui::Button(localize.get(LABEL_BAKE), widgetSize))
|
||||
{
|
||||
frames_bake();
|
||||
@@ -1834,6 +1839,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CANCEL]);
|
||||
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) bakePopup.close();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CANCEL_BAKE_FRAMES));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user