context menus, document refactoring, fixes
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::canvas;
|
using namespace anm2ed::canvas;
|
||||||
using namespace anm2ed::playback;
|
using namespace anm2ed::playback;
|
||||||
@@ -17,6 +17,7 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace anm2ed::animation_preview
|
namespace anm2ed::animation_preview
|
||||||
{
|
{
|
||||||
|
constexpr auto NULL_COLOR = vec4(0.0f, 0.0f, 1.0f, 0.90f);
|
||||||
constexpr auto TARGET_SIZE = vec2(32, 32);
|
constexpr auto TARGET_SIZE = vec2(32, 32);
|
||||||
constexpr auto PIVOT_SIZE = vec2(8, 8);
|
constexpr auto PIVOT_SIZE = vec2(8, 8);
|
||||||
constexpr auto POINT_SIZE = vec2(4, 4);
|
constexpr auto POINT_SIZE = vec2(4, 4);
|
||||||
@@ -27,10 +28,11 @@ namespace anm2ed::animation_preview
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationPreview::update(DocumentManager& manager, Settings& settings, Resources& resources, Playback& playback)
|
void AnimationPreview::update(Manager& manager, Settings& settings, Resources& resources)
|
||||||
{
|
{
|
||||||
auto& document = *manager.get();
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
|
auto& playback = document.playback;
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
auto animation = document.animation_get();
|
auto animation = document.animation_get();
|
||||||
auto& pan = document.previewPan;
|
auto& pan = document.previewPan;
|
||||||
@@ -143,12 +145,10 @@ namespace anm2ed::animation_preview
|
|||||||
if (isAxes) axes_render(shaderAxes, zoom, pan, axesColor);
|
if (isAxes) axes_render(shaderAxes, zoom, pan, axesColor);
|
||||||
if (isGrid) grid_render(shaderGrid, zoom, pan, gridSize, gridOffset, gridColor);
|
if (isGrid) grid_render(shaderGrid, zoom, pan, gridSize, gridOffset, gridColor);
|
||||||
|
|
||||||
auto frameTime = reference.frameTime > -1 && !playback.isPlaying ? reference.frameTime : playback.time;
|
auto render = [&](float time, vec3 colorOffset = {}, float alphaOffset = {}, bool isOnionskin = false)
|
||||||
|
|
||||||
if (animation)
|
|
||||||
{
|
{
|
||||||
auto transform = transform_get(zoom, pan);
|
auto transform = transform_get(zoom, pan);
|
||||||
auto root = animation->rootAnimation.frame_generate(playback.time, anm2::ROOT);
|
auto root = animation->rootAnimation.frame_generate(time, anm2::ROOT);
|
||||||
|
|
||||||
if (isRootTransform)
|
if (isRootTransform)
|
||||||
transform *= math::quad_model_parent_get(root.position, {}, math::percent_to_unit(root.scale), root.rotation);
|
transform *= math::quad_model_parent_get(root.position, {}, math::percent_to_unit(root.scale), root.rotation);
|
||||||
@@ -158,7 +158,9 @@ namespace anm2ed::animation_preview
|
|||||||
auto rootTransform = transform * math::quad_model_get(TARGET_SIZE, root.position, TARGET_SIZE * 0.5f,
|
auto rootTransform = transform * math::quad_model_get(TARGET_SIZE, root.position, TARGET_SIZE * 0.5f,
|
||||||
math::percent_to_unit(root.scale), root.rotation);
|
math::percent_to_unit(root.scale), root.rotation);
|
||||||
|
|
||||||
texture_render(shaderTexture, resources.icons[icon::TARGET].id, rootTransform, color::GREEN);
|
vec4 color = isOnionskin ? vec4(colorOffset, alphaOffset) : color::GREEN;
|
||||||
|
|
||||||
|
texture_render(shaderTexture, resources.icons[icon::TARGET].id, rootTransform, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& id : animation->layerOrder)
|
for (auto& id : animation->layerOrder)
|
||||||
@@ -168,7 +170,7 @@ namespace anm2ed::animation_preview
|
|||||||
|
|
||||||
auto& layer = anm2.content.layers.at(id);
|
auto& layer = anm2.content.layers.at(id);
|
||||||
|
|
||||||
if (auto frame = layerAnimation.frame_generate(frameTime, anm2::LAYER); frame.isVisible)
|
if (auto frame = layerAnimation.frame_generate(time, anm2::LAYER); frame.isVisible)
|
||||||
{
|
{
|
||||||
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
|
||||||
if (!spritesheet) continue;
|
if (!spritesheet) continue;
|
||||||
@@ -183,17 +185,23 @@ namespace anm2ed::animation_preview
|
|||||||
auto uvMin = frame.crop / vec2(texture.size) + inset;
|
auto uvMin = frame.crop / vec2(texture.size) + inset;
|
||||||
auto uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
auto uvMax = (frame.crop + frame.size) / vec2(texture.size) - inset;
|
||||||
auto vertices = math::uv_vertices_get(uvMin, uvMax);
|
auto vertices = math::uv_vertices_get(uvMin, uvMax);
|
||||||
|
vec3 frameColorOffset = frame.offset + colorOffset;
|
||||||
|
vec4 frameTint = frame.tint;
|
||||||
|
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
|
||||||
|
|
||||||
texture_render(shaderTexture, texture.id, layerTransform, frame.tint, frame.offset, vertices.data());
|
texture_render(shaderTexture, texture.id, layerTransform, frameTint, frameColorOffset, vertices.data());
|
||||||
|
|
||||||
if (isBorder) rect_render(shaderLine, layerTransform, color::RED);
|
auto color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : color::RED;
|
||||||
|
|
||||||
|
if (isBorder) rect_render(shaderLine, layerTransform, color);
|
||||||
|
|
||||||
if (isPivots)
|
if (isPivots)
|
||||||
{
|
{
|
||||||
auto pivotTransform =
|
auto pivotTransform =
|
||||||
transform * math::quad_model_get(PIVOT_SIZE, frame.position, PIVOT_SIZE * 0.5f,
|
transform * math::quad_model_get(PIVOT_SIZE, frame.position, PIVOT_SIZE * 0.5f,
|
||||||
math::percent_to_unit(frame.scale), frame.rotation);
|
math::percent_to_unit(frame.scale), frame.rotation);
|
||||||
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color::RED);
|
|
||||||
|
texture_render(shaderTexture, resources.icons[icon::PIVOT].id, pivotTransform, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,11 +214,13 @@ namespace anm2ed::animation_preview
|
|||||||
|
|
||||||
auto& isShowRect = anm2.content.nulls[id].isShowRect;
|
auto& isShowRect = anm2.content.nulls[id].isShowRect;
|
||||||
|
|
||||||
if (auto frame = nullAnimation.frame_generate(frameTime, anm2::NULL_); frame.isVisible)
|
if (auto frame = nullAnimation.frame_generate(time, anm2::NULL_); frame.isVisible)
|
||||||
{
|
{
|
||||||
auto icon = isShowRect ? icon::POINT : icon::TARGET;
|
auto icon = isShowRect ? icon::POINT : icon::TARGET;
|
||||||
auto& size = isShowRect ? POINT_SIZE : TARGET_SIZE;
|
auto& size = isShowRect ? POINT_SIZE : TARGET_SIZE;
|
||||||
auto& color = id == reference.itemID && reference.itemType == anm2::NULL_ ? color::RED : color::BLUE;
|
auto color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset)
|
||||||
|
: id == reference.itemID && reference.itemType == anm2::NULL_ ? color::RED
|
||||||
|
: NULL_COLOR;
|
||||||
|
|
||||||
auto nullTransform = transform * math::quad_model_get(size, frame.position, size * 0.5f,
|
auto nullTransform = transform * math::quad_model_get(size, frame.position, size * 0.5f,
|
||||||
math::percent_to_unit(frame.scale), frame.rotation);
|
math::percent_to_unit(frame.scale), frame.rotation);
|
||||||
@@ -228,6 +238,34 @@ namespace anm2ed::animation_preview
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onionskin_render = [&](float time, int count, int direction, vec3 color)
|
||||||
|
{
|
||||||
|
for (int i = 1; i <= count; i++)
|
||||||
|
{
|
||||||
|
float useTime = time + (float)(direction * i);
|
||||||
|
float alphaOffset = (1.0f / (count + 1)) * i;
|
||||||
|
render(useTime, color, alphaOffset, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto onionskins_render = [&](float time)
|
||||||
|
{
|
||||||
|
onionskin_render(time, settings.onionskinBeforeCount, -1, settings.onionskinBeforeColor);
|
||||||
|
onionskin_render(time, settings.onionskinAfterCount, 1, settings.onionskinAfterColor);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto frameTime = reference.frameTime > -1 && !playback.isPlaying ? reference.frameTime : playback.time;
|
||||||
|
|
||||||
|
if (animation)
|
||||||
|
{
|
||||||
|
auto& drawOrder = settings.onionskinDrawOrder;
|
||||||
|
auto& isEnabled = settings.onionskinIsEnabled;
|
||||||
|
|
||||||
|
if (drawOrder == draw_order::BELOW && isEnabled) onionskins_render(frameTime);
|
||||||
|
render(frameTime);
|
||||||
|
if (drawOrder == draw_order::ABOVE && isEnabled) onionskins_render(frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
unbind();
|
unbind();
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "document_manager.h"
|
#include "manager.h"
|
||||||
#include "playback.h"
|
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -15,7 +14,6 @@ namespace anm2ed::animation_preview
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
AnimationPreview();
|
AnimationPreview();
|
||||||
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&);
|
||||||
resources::Resources& resources, playback::Playback& playback);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,24 @@
|
|||||||
#include "animations.h"
|
#include "animations.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace anm2ed::document;
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
|
|
||||||
namespace anm2ed::animations
|
namespace anm2ed::animations
|
||||||
{
|
{
|
||||||
void Animations::update(Document& document, int& documentIndex, Settings& settings, Resources& resources)
|
void Animations::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
auto& selection = document.selectedAnimations;
|
auto& hovered = document.hoveredAnimation;
|
||||||
storage.user_data_set(&selection);
|
auto& multiSelect = document.animationMultiSelect;
|
||||||
|
auto& mergeMultiSelect = document.animationMergeMultiSelect;
|
||||||
|
auto& mergeTarget = document.mergeTarget;
|
||||||
|
|
||||||
if (ImGui::Begin("Animations", &settings.windowIsAnimations))
|
if (ImGui::Begin("Animations", &settings.windowIsAnimations))
|
||||||
{
|
{
|
||||||
@@ -23,7 +26,7 @@ namespace anm2ed::animations
|
|||||||
|
|
||||||
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
storage.begin(anm2.animations.items.size());
|
multiSelect.start(anm2.animations.items.size());
|
||||||
|
|
||||||
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
|
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
|
||||||
{
|
{
|
||||||
@@ -31,7 +34,6 @@ namespace anm2ed::animations
|
|||||||
|
|
||||||
auto isDefault = anm2.animations.defaultAnimation == animation.name;
|
auto isDefault = anm2.animations.defaultAnimation == animation.name;
|
||||||
auto isReferenced = reference.animationIndex == i;
|
auto isReferenced = reference.animationIndex == i;
|
||||||
auto isSelected = selection.contains(i);
|
|
||||||
|
|
||||||
auto font = isDefault && isReferenced ? font::BOLD_ITALICS
|
auto font = isDefault && isReferenced ? font::BOLD_ITALICS
|
||||||
: isDefault ? font::BOLD
|
: isDefault ? font::BOLD
|
||||||
@@ -41,9 +43,10 @@ namespace anm2ed::animations
|
|||||||
ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
|
ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
|
||||||
ImGui::SetNextItemSelectionUserData(i);
|
ImGui::SetNextItemSelectionUserData(i);
|
||||||
if (imgui::selectable_input_text(animation.name,
|
if (imgui::selectable_input_text(animation.name,
|
||||||
std::format("###Document #{} Animation #{}", documentIndex, i),
|
std::format("###Document #{} Animation #{}", manager.selected, i),
|
||||||
animation.name, isSelected))
|
animation.name, multiSelect.contains(i)))
|
||||||
if (!isReferenced) reference = {(int)i};
|
reference = {(int)i};
|
||||||
|
if (ImGui::IsItemHovered()) hovered = i;
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
@@ -68,10 +71,11 @@ namespace anm2ed::animations
|
|||||||
|
|
||||||
if (ImGui::BeginDragDropSource())
|
if (ImGui::BeginDragDropSource())
|
||||||
{
|
{
|
||||||
std::vector<int> sorted = {};
|
static std::vector<int> selection;
|
||||||
ImGui::SetDragDropPayload("Animation Drag Drop", sorted.data(), sorted.size() * sizeof(int));
|
selection.assign(multiSelect.begin(), multiSelect.end());
|
||||||
for (auto& index : sorted)
|
ImGui::SetDragDropPayload("Animation Drag Drop", selection.data(), selection.size() * sizeof(int));
|
||||||
ImGui::TextUnformatted(anm2.animations.items[index].name.c_str());
|
for (auto& i : selection)
|
||||||
|
ImGui::TextUnformatted(anm2.animations.items[i].name.c_str());
|
||||||
ImGui::EndDragDropSource();
|
ImGui::EndDragDropSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,16 +83,11 @@ namespace anm2ed::animations
|
|||||||
{
|
{
|
||||||
if (auto payload = ImGui::AcceptDragDropPayload("Animation Drag Drop"))
|
if (auto payload = ImGui::AcceptDragDropPayload("Animation Drag Drop"))
|
||||||
{
|
{
|
||||||
auto count = payload->DataSize / sizeof(int);
|
auto payloadIndices = (int*)(payload->Data);
|
||||||
auto data = (int*)(payload->Data);
|
auto payloadCount = payload->DataSize / sizeof(int);
|
||||||
std::vector<int> indices(data, data + count);
|
std::vector<int> indices(payloadIndices, payloadIndices + payloadCount);
|
||||||
//std::vector<int> destinationIndices = vector::indices_move(anm2.animations.items, indices, i);
|
std::sort(indices.begin(), indices.end());
|
||||||
|
document.animations_move(indices, i);
|
||||||
selection.clear();
|
|
||||||
/*
|
|
||||||
for (const auto& index : destinationIndices)
|
|
||||||
selection.insert((int)index);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
ImGui::EndDragDropTarget();
|
ImGui::EndDragDropTarget();
|
||||||
}
|
}
|
||||||
@@ -96,63 +95,90 @@ namespace anm2ed::animations
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.end();
|
multiSelect.finish();
|
||||||
|
|
||||||
|
auto copy = [&]()
|
||||||
|
{
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
std::string clipboardText{};
|
||||||
|
for (auto& i : multiSelect)
|
||||||
|
clipboardText += anm2.animations.items[i].to_string();
|
||||||
|
clipboard.set(clipboardText);
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
clipboard.set(anm2.animations.items[hovered].to_string());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto cut = [&]()
|
||||||
|
{
|
||||||
|
copy();
|
||||||
|
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
for (auto& i : multiSelect | std::views::reverse)
|
||||||
|
anm2.animations.items.erase(anm2.animations.items.begin() + i);
|
||||||
|
multiSelect.clear();
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
{
|
||||||
|
anm2.animations.items.erase(anm2.animations.items.begin() + hovered);
|
||||||
|
hovered = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto paste = [&]()
|
||||||
|
{
|
||||||
|
auto clipboardText = clipboard.get();
|
||||||
|
document.animations_deserialize(clipboardText);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutCut, shortcut::FOCUSED)) cut();
|
||||||
|
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
|
||||||
|
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste();
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
|
||||||
|
if (ImGui::MenuItem("Cut", settings.shortcutCut.c_str())) cut();
|
||||||
|
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(clipboard.is_empty());
|
||||||
|
if (ImGui::MenuItem("Paste", settings.shortcutPaste.c_str())) paste();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(5);
|
auto widgetSize = imgui::widget_size_with_row_get(5);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
if (ImGui::Button("Add", widgetSize))
|
if (ImGui::Button("Add", widgetSize)) document.animation_add();
|
||||||
{
|
|
||||||
anm2::Animation animation;
|
|
||||||
if (anm2::Animation* referenceAnimation = document.animation_get())
|
|
||||||
{
|
|
||||||
for (auto [id, layerAnimation] : referenceAnimation->layerAnimations)
|
|
||||||
animation.layerAnimations[id] = anm2::Item();
|
|
||||||
animation.layerOrder = referenceAnimation->layerOrder;
|
|
||||||
for (auto [id, nullAnimation] : referenceAnimation->nullAnimations)
|
|
||||||
animation.nullAnimations[id] = anm2::Item();
|
|
||||||
}
|
|
||||||
animation.rootAnimation.frames.emplace_back(anm2::Frame());
|
|
||||||
|
|
||||||
auto index = selection.empty() ? (int)anm2.animations.items.size() - 1 : (int)std::ranges::max(selection) + 1;
|
|
||||||
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
|
|
||||||
selection = {index};
|
|
||||||
reference = {index};
|
|
||||||
document.change(change::ANIMATIONS);
|
|
||||||
}
|
|
||||||
imgui::set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd);
|
imgui::set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd);
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.empty());
|
ImGui::BeginDisabled(multiSelect.empty());
|
||||||
{
|
{
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutDuplicate);
|
imgui::shortcut(settings.shortcutDuplicate);
|
||||||
if (ImGui::Button("Duplicate", widgetSize))
|
if (ImGui::Button("Duplicate", widgetSize)) document.animation_duplicate();
|
||||||
{
|
|
||||||
auto duplicated = selection;
|
|
||||||
auto duplicatedEnd = std::ranges::max(duplicated);
|
|
||||||
for (auto& id : duplicated)
|
|
||||||
{
|
|
||||||
anm2.animations.items.insert(anm2.animations.items.begin() + duplicatedEnd, anm2.animations.items[id]);
|
|
||||||
selection.insert(++duplicatedEnd);
|
|
||||||
selection.erase(id);
|
|
||||||
}
|
|
||||||
document.change(change::ANIMATIONS);
|
|
||||||
}
|
|
||||||
imgui::set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate);
|
imgui::set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate);
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.size() != 1);
|
if (imgui::shortcut(settings.shortcutMerge, shortcut::FOCUSED))
|
||||||
|
if (multiSelect.size() > 0) document.animations_merge_quick();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(multiSelect.size() != 1);
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Merge", widgetSize))
|
if (ImGui::Button("Merge", widgetSize))
|
||||||
{
|
{
|
||||||
mergePopup.open();
|
mergePopup.open();
|
||||||
mergeSelection.clear();
|
mergeMultiSelect.clear();
|
||||||
mergeTarget = *selection.begin();
|
mergeTarget = *multiSelect.begin();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
@@ -165,28 +191,16 @@ namespace anm2ed::animations
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
if (ImGui::Button("Remove", widgetSize))
|
if (ImGui::Button("Remove", widgetSize)) document.animation_remove();
|
||||||
{
|
|
||||||
/*
|
|
||||||
auto selectionErase = set::to_size_t(selection);
|
|
||||||
if (selectionErase.contains(document.reference.animationIndex)) document.reference.animationIndex = -1;
|
|
||||||
vector::range_erase(anm2.animations.items, selectionErase);
|
|
||||||
*/
|
|
||||||
document.change(change::ANIMATIONS);
|
|
||||||
selection.clear();
|
|
||||||
}
|
|
||||||
imgui::set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutDuplicate);
|
imgui::set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutDuplicate);
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.size() != 1);
|
imgui::shortcut(settings.shortcutDefault);
|
||||||
{
|
ImGui::BeginDisabled(multiSelect.size() != 1);
|
||||||
imgui::shortcut(settings.shortcutDefault);
|
if (ImGui::Button("Default", widgetSize)) document.animation_default();
|
||||||
if (ImGui::Button("Default", widgetSize))
|
|
||||||
anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name;
|
|
||||||
imgui::set_item_tooltip_shortcut("Set the selected animation as the default.", settings.shortcutDuplicate);
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
imgui::set_item_tooltip_shortcut("Set the selected animation as the default.", settings.shortcutDefault);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
@@ -196,15 +210,13 @@ namespace anm2ed::animations
|
|||||||
{
|
{
|
||||||
auto merge_close = [&]()
|
auto merge_close = [&]()
|
||||||
{
|
{
|
||||||
mergeSelection.clear();
|
mergeMultiSelect.clear();
|
||||||
mergePopup.close();
|
mergePopup.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& type = settings.mergeType;
|
auto& type = settings.mergeType;
|
||||||
auto& isDeleteAnimationsAfter = settings.mergeIsDeleteAnimationsAfter;
|
auto& isDeleteAnimationsAfter = settings.mergeIsDeleteAnimationsAfter;
|
||||||
|
|
||||||
mergeStorage.user_data_set(&mergeSelection);
|
|
||||||
|
|
||||||
auto footerSize = imgui::footer_size_get();
|
auto footerSize = imgui::footer_size_get();
|
||||||
auto optionsSize = imgui::child_size_get(2);
|
auto optionsSize = imgui::child_size_get(2);
|
||||||
auto deleteAfterSize = imgui::child_size_get();
|
auto deleteAfterSize = imgui::child_size_get();
|
||||||
@@ -214,21 +226,19 @@ namespace anm2ed::animations
|
|||||||
|
|
||||||
if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
mergeSelection.begin();
|
mergeMultiSelect.start(anm2.animations.items.size());
|
||||||
|
|
||||||
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
|
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
|
||||||
{
|
{
|
||||||
auto isSelected = mergeSelection.contains(i);
|
|
||||||
|
|
||||||
ImGui::PushID(i);
|
ImGui::PushID(i);
|
||||||
|
|
||||||
ImGui::SetNextItemSelectionUserData(i);
|
ImGui::SetNextItemSelectionUserData(i);
|
||||||
ImGui::Selectable(animation.name.c_str(), isSelected);
|
ImGui::Selectable(animation.name.c_str(), mergeMultiSelect.contains(i));
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeSelection.end();
|
mergeMultiSelect.finish();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
@@ -262,12 +272,7 @@ namespace anm2ed::animations
|
|||||||
|
|
||||||
if (ImGui::Button("Merge", widgetSize))
|
if (ImGui::Button("Merge", widgetSize))
|
||||||
{
|
{
|
||||||
/*
|
document.animations_merge((merge::Type)type, isDeleteAnimationsAfter);
|
||||||
std::set<int> sources = set::to_set(mergeSelection);
|
|
||||||
const auto merged = anm2.animations.merge(mergeTarget, sources, (MergeType)type, isDeleteAnimationsAfter);
|
|
||||||
selection = {merged};
|
|
||||||
reference = {merged};
|
|
||||||
*/
|
|
||||||
merge_close();
|
merge_close();
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document.h"
|
#include "clipboard.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -10,13 +11,8 @@ namespace anm2ed::animations
|
|||||||
class Animations
|
class Animations
|
||||||
{
|
{
|
||||||
imgui::PopupHelper mergePopup{imgui::PopupHelper("Merge Animations")};
|
imgui::PopupHelper mergePopup{imgui::PopupHelper("Merge Animations")};
|
||||||
imgui::MultiSelectStorage mergeStorage{};
|
|
||||||
imgui::MultiSelectStorage storage{};
|
|
||||||
std::set<int> mergeSelection{};
|
|
||||||
int mergeTarget{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document::Document& document, int& documentIndex, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&, clipboard::Clipboard&);
|
||||||
resources::Resources& resources);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
334
src/anm2.cpp
334
src/anm2.cpp
@@ -13,6 +13,7 @@ using namespace anm2ed::filesystem;
|
|||||||
using namespace anm2ed::texture;
|
using namespace anm2ed::texture;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
using namespace anm2ed::util;
|
using namespace anm2ed::util;
|
||||||
|
using namespace glm;
|
||||||
|
|
||||||
namespace anm2ed::anm2
|
namespace anm2ed::anm2
|
||||||
{
|
{
|
||||||
@@ -64,14 +65,14 @@ namespace anm2ed::anm2
|
|||||||
// If it doesn't work beyond that then that's on the user :^)
|
// If it doesn't work beyond that then that's on the user :^)
|
||||||
if (!path_is_exist(path)) path = string::to_lower(path);
|
if (!path_is_exist(path)) path = string::to_lower(path);
|
||||||
if (!path_is_exist(path)) path = string::replace_backslash(path);
|
if (!path_is_exist(path)) path = string::replace_backslash(path);
|
||||||
texture = Texture(path, true);
|
texture = Texture(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Spritesheet::Spritesheet(const std::string& directory, const std::string& path)
|
Spritesheet::Spritesheet(const std::string& directory, const std::string& path)
|
||||||
{
|
{
|
||||||
this->path = !path.empty() ? path : this->path.string();
|
this->path = !path.empty() ? path : this->path.string();
|
||||||
WorkingDirectory workingDirectory(directory);
|
WorkingDirectory workingDirectory(directory);
|
||||||
texture = Texture(this->path, true);
|
texture = Texture(this->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Spritesheet::save(const std::string& directory, const std::string& path)
|
bool Spritesheet::save(const std::string& directory, const std::string& path)
|
||||||
@@ -99,6 +100,22 @@ namespace anm2ed::anm2
|
|||||||
return texture.is_valid();
|
return texture.is_valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Spritesheet::to_string(int id)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
auto* element = document.NewElement("Spritesheet");
|
||||||
|
|
||||||
|
element->SetAttribute("Id", id);
|
||||||
|
element->SetAttribute("Path", path.c_str());
|
||||||
|
|
||||||
|
document.InsertFirstChild(element);
|
||||||
|
|
||||||
|
XMLPrinter printer;
|
||||||
|
document.Print(&printer);
|
||||||
|
return std::string(printer.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
Layer::Layer() = default;
|
Layer::Layer() = default;
|
||||||
|
|
||||||
Layer::Layer(XMLElement* element, int& id)
|
Layer::Layer(XMLElement* element, int& id)
|
||||||
@@ -118,6 +135,23 @@ namespace anm2ed::anm2
|
|||||||
parent->InsertEndChild(element);
|
parent->InsertEndChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Layer::to_string(int id)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
auto* element = document.NewElement("Layer");
|
||||||
|
|
||||||
|
element->SetAttribute("Id", id);
|
||||||
|
element->SetAttribute("Name", name.c_str());
|
||||||
|
element->SetAttribute("SpritesheetId", spritesheetID);
|
||||||
|
|
||||||
|
document.InsertFirstChild(element);
|
||||||
|
|
||||||
|
XMLPrinter printer;
|
||||||
|
document.Print(&printer);
|
||||||
|
return std::string(printer.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
Null::Null() = default;
|
Null::Null() = default;
|
||||||
|
|
||||||
Null::Null(XMLElement* element, int& id)
|
Null::Null(XMLElement* element, int& id)
|
||||||
@@ -138,6 +172,22 @@ namespace anm2ed::anm2
|
|||||||
parent->InsertEndChild(element);
|
parent->InsertEndChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Null::to_string(int id)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
auto* element = document.NewElement("Null");
|
||||||
|
|
||||||
|
element->SetAttribute("Id", id);
|
||||||
|
element->SetAttribute("Name", name.c_str());
|
||||||
|
|
||||||
|
document.InsertFirstChild(element);
|
||||||
|
|
||||||
|
XMLPrinter printer;
|
||||||
|
document.Print(&printer);
|
||||||
|
return std::string(printer.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
Event::Event() = default;
|
Event::Event() = default;
|
||||||
|
|
||||||
Event::Event(XMLElement* element, int& id)
|
Event::Event(XMLElement* element, int& id)
|
||||||
@@ -155,6 +205,22 @@ namespace anm2ed::anm2
|
|||||||
parent->InsertEndChild(element);
|
parent->InsertEndChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Event::to_string(int id)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
auto* element = document.NewElement("Event");
|
||||||
|
|
||||||
|
element->SetAttribute("Id", id);
|
||||||
|
element->SetAttribute("Name", name.c_str());
|
||||||
|
|
||||||
|
document.InsertFirstChild(element);
|
||||||
|
|
||||||
|
XMLPrinter printer;
|
||||||
|
document.Print(&printer);
|
||||||
|
return std::string(printer.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
Content::Content() = default;
|
Content::Content() = default;
|
||||||
|
|
||||||
void Content::serialize(XMLDocument& document, XMLElement* parent)
|
void Content::serialize(XMLDocument& document, XMLElement* parent)
|
||||||
@@ -245,6 +311,134 @@ namespace anm2ed::anm2
|
|||||||
events[id] = Event();
|
events[id] = Event();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Content::spritesheets_deserialize(const std::string& string, const std::string& directory, merge::Type type,
|
||||||
|
std::string* errorString)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
if (document.Parse(string.c_str()) == XML_SUCCESS)
|
||||||
|
{
|
||||||
|
int id{};
|
||||||
|
|
||||||
|
if (!document.FirstChildElement("Spritesheet"))
|
||||||
|
{
|
||||||
|
if (errorString) *errorString = "No valid spritesheet(s).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkingDirectory workingDirectory(directory);
|
||||||
|
|
||||||
|
for (auto element = document.FirstChildElement("Spritesheet"); element;
|
||||||
|
element = element->NextSiblingElement("Spritesheet"))
|
||||||
|
{
|
||||||
|
auto spritesheet = Spritesheet(element, id);
|
||||||
|
|
||||||
|
if (type == merge::APPEND) id = map::next_id_get(spritesheets);
|
||||||
|
|
||||||
|
spritesheets[id] = std::move(spritesheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errorString)
|
||||||
|
*errorString = document.ErrorStr();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Content::layers_deserialize(const std::string& string, merge::Type type, std::string* errorString)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
if (document.Parse(string.c_str()) == XML_SUCCESS)
|
||||||
|
{
|
||||||
|
int id{};
|
||||||
|
|
||||||
|
if (!document.FirstChildElement("Layer"))
|
||||||
|
{
|
||||||
|
if (errorString) *errorString = "No valid layer(s).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto element = document.FirstChildElement("Layer"); element; element = element->NextSiblingElement("Layer"))
|
||||||
|
{
|
||||||
|
auto layer = Layer(element, id);
|
||||||
|
|
||||||
|
if (type == merge::APPEND) id = map::next_id_get(layers);
|
||||||
|
|
||||||
|
layers[id] = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errorString)
|
||||||
|
*errorString = document.ErrorStr();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Content::nulls_deserialize(const std::string& string, merge::Type type, std::string* errorString)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
if (document.Parse(string.c_str()) == XML_SUCCESS)
|
||||||
|
{
|
||||||
|
int id{};
|
||||||
|
|
||||||
|
if (!document.FirstChildElement("Null"))
|
||||||
|
{
|
||||||
|
if (errorString) *errorString = "No valid null(s).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto element = document.FirstChildElement("Null"); element; element = element->NextSiblingElement("Null"))
|
||||||
|
{
|
||||||
|
auto layer = Null(element, id);
|
||||||
|
|
||||||
|
if (type == merge::APPEND) id = map::next_id_get(nulls);
|
||||||
|
|
||||||
|
nulls[id] = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errorString)
|
||||||
|
*errorString = document.ErrorStr();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Content::events_deserialize(const std::string& string, merge::Type type, std::string* errorString)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
if (document.Parse(string.c_str()) == XML_SUCCESS)
|
||||||
|
{
|
||||||
|
int id{};
|
||||||
|
|
||||||
|
if (!document.FirstChildElement("Event"))
|
||||||
|
{
|
||||||
|
if (errorString) *errorString = "No valid event(s).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto element = document.FirstChildElement("Event"); element; element = element->NextSiblingElement("Event"))
|
||||||
|
{
|
||||||
|
auto layer = Event(element, id);
|
||||||
|
|
||||||
|
if (type == merge::APPEND) id = map::next_id_get(events);
|
||||||
|
|
||||||
|
events[id] = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errorString)
|
||||||
|
*errorString = document.ErrorStr();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Frame::Frame() = default;
|
Frame::Frame() = default;
|
||||||
|
|
||||||
Frame::Frame(XMLElement* element, Type type)
|
Frame::Frame(XMLElement* element, Type type)
|
||||||
@@ -504,6 +698,25 @@ namespace anm2ed::anm2
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation::item_remove(Type type, int id)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case LAYER:
|
||||||
|
layerAnimations.erase(id);
|
||||||
|
for (auto [i, value] : std::views::enumerate(layerOrder))
|
||||||
|
if (value == id) layerOrder.erase(layerOrder.begin() + i);
|
||||||
|
break;
|
||||||
|
case NULL_:
|
||||||
|
nullAnimations.erase(id);
|
||||||
|
break;
|
||||||
|
case ROOT:
|
||||||
|
case TRIGGER:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Animation::serialize(XMLDocument& document, XMLElement* parent)
|
void Animation::serialize(XMLDocument& document, XMLElement* parent)
|
||||||
{
|
{
|
||||||
auto element = document.NewElement("Animation");
|
auto element = document.NewElement("Animation");
|
||||||
@@ -551,6 +764,39 @@ namespace anm2ed::anm2
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Animation::to_string()
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
auto* element = document.NewElement("Animation");
|
||||||
|
document.InsertFirstChild(element);
|
||||||
|
|
||||||
|
element->SetAttribute("Name", name.c_str());
|
||||||
|
element->SetAttribute("FrameNum", frameNum);
|
||||||
|
element->SetAttribute("Loop", isLoop);
|
||||||
|
|
||||||
|
rootAnimation.serialize(document, element, ROOT);
|
||||||
|
|
||||||
|
auto layerAnimationsElement = document.NewElement("LayerAnimations");
|
||||||
|
for (auto& i : layerOrder)
|
||||||
|
{
|
||||||
|
Item& layerAnimation = layerAnimations.at(i);
|
||||||
|
layerAnimation.serialize(document, layerAnimationsElement, LAYER, i);
|
||||||
|
}
|
||||||
|
element->InsertEndChild(layerAnimationsElement);
|
||||||
|
|
||||||
|
auto nullAnimationsElement = document.NewElement("NullAnimations");
|
||||||
|
for (auto& [id, nullAnimation] : nullAnimations)
|
||||||
|
nullAnimation.serialize(document, nullAnimationsElement, NULL_, id);
|
||||||
|
element->InsertEndChild(nullAnimationsElement);
|
||||||
|
|
||||||
|
triggers.serialize(document, element, TRIGGER);
|
||||||
|
|
||||||
|
XMLPrinter printer;
|
||||||
|
document.Print(&printer);
|
||||||
|
return std::string(printer.CStr());
|
||||||
|
}
|
||||||
|
|
||||||
Animations::Animations() = default;
|
Animations::Animations() = default;
|
||||||
|
|
||||||
Animations::Animations(XMLElement* element)
|
Animations::Animations(XMLElement* element)
|
||||||
@@ -660,6 +906,37 @@ namespace anm2ed::anm2
|
|||||||
return finalIndex;
|
return finalIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Animations::animations_deserialize(const std::string& string, int start, std::set<int>& indices,
|
||||||
|
std::string* errorString)
|
||||||
|
{
|
||||||
|
XMLDocument document{};
|
||||||
|
|
||||||
|
if (document.Parse(string.c_str()) == XML_SUCCESS)
|
||||||
|
{
|
||||||
|
if (!document.FirstChildElement("Animation"))
|
||||||
|
{
|
||||||
|
if (errorString) *errorString = "No valid animation(s).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count{};
|
||||||
|
for (auto element = document.FirstChildElement("Animation"); element;
|
||||||
|
element = element->NextSiblingElement("Animation"))
|
||||||
|
{
|
||||||
|
auto index = start + count;
|
||||||
|
items.insert(items.begin() + start + count, Animation(element));
|
||||||
|
indices.insert(index);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (errorString)
|
||||||
|
*errorString = document.ErrorStr();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Anm2::Anm2()
|
Anm2::Anm2()
|
||||||
{
|
{
|
||||||
info.createdOn = time::get("%d-%B-%Y %I:%M:%S");
|
info.createdOn = time::get("%d-%B-%Y %I:%M:%S");
|
||||||
@@ -697,7 +974,7 @@ namespace anm2ed::anm2
|
|||||||
|
|
||||||
XMLPrinter printer;
|
XMLPrinter printer;
|
||||||
document.Print(&printer);
|
document.Print(&printer);
|
||||||
return printer.CStr();
|
return std::string(printer.CStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
Anm2::Anm2(const std::string& path, std::string* errorString)
|
Anm2::Anm2(const std::string& path, std::string* errorString)
|
||||||
@@ -710,8 +987,6 @@ namespace anm2ed::anm2
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid = false;
|
|
||||||
|
|
||||||
WorkingDirectory workingDirectory(path, true);
|
WorkingDirectory workingDirectory(path, true);
|
||||||
|
|
||||||
const XMLElement* element = document.RootElement();
|
const XMLElement* element = document.RootElement();
|
||||||
@@ -907,4 +1182,53 @@ namespace anm2ed::anm2
|
|||||||
spritesheets.push_back(std::format("#{} {}", id, spritesheet.path.c_str()));
|
spritesheets.push_back(std::format("#{} {}", id, spritesheet.path.c_str()));
|
||||||
return spritesheets;
|
return spritesheets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Anm2::bake(Reference reference, int interval, bool isRoundScale, bool isRoundRotation)
|
||||||
|
{
|
||||||
|
Item* item = item_get(reference);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
Frame* frame = frame_get(reference);
|
||||||
|
if (!frame) return;
|
||||||
|
|
||||||
|
if (frame->delay == FRAME_DELAY_MIN) return;
|
||||||
|
|
||||||
|
Reference referenceNext = reference;
|
||||||
|
referenceNext.frameIndex = reference.frameIndex + 1;
|
||||||
|
|
||||||
|
Frame* frameNext = frame_get(referenceNext);
|
||||||
|
if (!frameNext) frameNext = frame;
|
||||||
|
|
||||||
|
Frame baseFrame = *frame;
|
||||||
|
Frame baseFrameNext = *frameNext;
|
||||||
|
|
||||||
|
int delay{};
|
||||||
|
int index = reference.frameIndex;
|
||||||
|
|
||||||
|
while (delay < baseFrame.delay)
|
||||||
|
{
|
||||||
|
float interpolation = (float)delay / baseFrame.delay;
|
||||||
|
|
||||||
|
Frame baked = baseFrame;
|
||||||
|
baked.delay = std::min(interval, baseFrame.delay - delay);
|
||||||
|
baked.isInterpolated = (index == reference.frameIndex) ? baseFrame.isInterpolated : false;
|
||||||
|
|
||||||
|
baked.rotation = glm::mix(baseFrame.rotation, baseFrameNext.rotation, interpolation);
|
||||||
|
baked.position = glm::mix(baseFrame.position, baseFrameNext.position, interpolation);
|
||||||
|
baked.scale = glm::mix(baseFrame.scale, baseFrameNext.scale, interpolation);
|
||||||
|
baked.offset = glm::mix(baseFrame.offset, baseFrameNext.offset, interpolation);
|
||||||
|
baked.tint = glm::mix(baseFrame.tint, baseFrameNext.tint, interpolation);
|
||||||
|
|
||||||
|
if (isRoundScale) baked.scale = vec2(ivec2(baked.scale));
|
||||||
|
if (isRoundRotation) baked.rotation = (int)baked.rotation;
|
||||||
|
|
||||||
|
if (index == reference.frameIndex)
|
||||||
|
item->frames[index] = baked;
|
||||||
|
else
|
||||||
|
item->frames.insert(item->frames.begin() + index, baked);
|
||||||
|
index++;
|
||||||
|
|
||||||
|
delay += baked.delay;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
114
src/anm2.h
114
src/anm2.h
@@ -21,6 +21,7 @@ namespace anm2ed::anm2
|
|||||||
|
|
||||||
constexpr auto MERGED_STRING = "(Merged)";
|
constexpr auto MERGED_STRING = "(Merged)";
|
||||||
|
|
||||||
|
constexpr auto NO_PATH = "(No Path)";
|
||||||
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
|
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
|
||||||
constexpr auto NULL_FORMAT = "#{} {}";
|
constexpr auto NULL_FORMAT = "#{} {}";
|
||||||
constexpr auto SPRITESHEET_FORMAT = "#%d %s";
|
constexpr auto SPRITESHEET_FORMAT = "#%d %s";
|
||||||
@@ -43,8 +44,8 @@ namespace anm2ed::anm2
|
|||||||
int frameIndex{-1};
|
int frameIndex{-1};
|
||||||
int frameTime{-1};
|
int frameTime{-1};
|
||||||
|
|
||||||
void previous_frame(int max = FRAME_NUM_MAX - 1);
|
void previous_frame(int = FRAME_NUM_MAX - 1);
|
||||||
void next_frame(int max = FRAME_NUM_MAX - 1);
|
void next_frame(int = FRAME_NUM_MAX - 1);
|
||||||
auto operator<=>(const Reference&) const = default;
|
auto operator<=>(const Reference&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,8 +60,8 @@ namespace anm2ed::anm2
|
|||||||
int version{};
|
int version{};
|
||||||
|
|
||||||
Info();
|
Info();
|
||||||
Info(tinyxml2::XMLElement* element);
|
Info(tinyxml2::XMLElement*);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Spritesheet
|
class Spritesheet
|
||||||
@@ -70,12 +71,13 @@ namespace anm2ed::anm2
|
|||||||
texture::Texture texture;
|
texture::Texture texture;
|
||||||
|
|
||||||
Spritesheet();
|
Spritesheet();
|
||||||
Spritesheet(tinyxml2::XMLElement* element, int& id);
|
Spritesheet(tinyxml2::XMLElement*, int&);
|
||||||
Spritesheet(const std::string& directory, const std::string& path = {});
|
Spritesheet(const std::string&, const std::string& = {});
|
||||||
bool save(const std::string& directory, const std::string& path = {});
|
bool save(const std::string&, const std::string& = {});
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, int);
|
||||||
void reload(const std::string& directory);
|
void reload(const std::string&);
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
|
std::string to_string(int id);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Layer
|
class Layer
|
||||||
@@ -85,8 +87,9 @@ namespace anm2ed::anm2
|
|||||||
int spritesheetID{};
|
int spritesheetID{};
|
||||||
|
|
||||||
Layer();
|
Layer();
|
||||||
Layer(tinyxml2::XMLElement* element, int& id);
|
Layer(tinyxml2::XMLElement*, int&);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, int);
|
||||||
|
std::string to_string(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Null
|
class Null
|
||||||
@@ -96,8 +99,9 @@ namespace anm2ed::anm2
|
|||||||
bool isShowRect{};
|
bool isShowRect{};
|
||||||
|
|
||||||
Null();
|
Null();
|
||||||
Null(tinyxml2::XMLElement* element, int& id);
|
Null(tinyxml2::XMLElement*, int&);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, int);
|
||||||
|
std::string to_string(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Event
|
class Event
|
||||||
@@ -106,8 +110,9 @@ namespace anm2ed::anm2
|
|||||||
std::string name{"New Event"};
|
std::string name{"New Event"};
|
||||||
|
|
||||||
Event();
|
Event();
|
||||||
Event(tinyxml2::XMLElement* element, int& id);
|
Event(tinyxml2::XMLElement*, int&);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, int id);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, int);
|
||||||
|
std::string to_string(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Content
|
struct Content
|
||||||
@@ -119,14 +124,18 @@ namespace anm2ed::anm2
|
|||||||
|
|
||||||
Content();
|
Content();
|
||||||
|
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*);
|
||||||
Content(tinyxml2::XMLElement* element);
|
Content(tinyxml2::XMLElement*);
|
||||||
bool spritesheet_add(const std::string& directory, const std::string& path, int& id);
|
bool spritesheet_add(const std::string&, const std::string&, int&);
|
||||||
void spritesheet_remove(int& id);
|
void spritesheet_remove(int&);
|
||||||
std::set<int> spritesheets_unused();
|
std::set<int> spritesheets_unused();
|
||||||
void layer_add(int& id);
|
void layer_add(int&);
|
||||||
void null_add(int& id);
|
void null_add(int&);
|
||||||
void event_add(int& id);
|
void event_add(int&);
|
||||||
|
bool spritesheets_deserialize(const std::string&, const std::string&, types::merge::Type, std::string* = nullptr);
|
||||||
|
bool layers_deserialize(const std::string&, types::merge::Type, std::string* = nullptr);
|
||||||
|
bool nulls_deserialize(const std::string&, types::merge::Type, std::string* = nullptr);
|
||||||
|
bool events_deserialize(const std::string&, types::merge::Type, std::string* = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MEMBERS \
|
#define MEMBERS \
|
||||||
@@ -152,8 +161,8 @@ namespace anm2ed::anm2
|
|||||||
#undef X
|
#undef X
|
||||||
|
|
||||||
Frame();
|
Frame();
|
||||||
Frame(tinyxml2::XMLElement* element, Type type);
|
Frame(tinyxml2::XMLElement*, Type);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, Type type);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, Type);
|
||||||
void shorten();
|
void shorten();
|
||||||
void extend();
|
void extend();
|
||||||
};
|
};
|
||||||
@@ -175,10 +184,10 @@ namespace anm2ed::anm2
|
|||||||
|
|
||||||
Item();
|
Item();
|
||||||
|
|
||||||
Item(tinyxml2::XMLElement* element, Type type, int* id = nullptr);
|
Item(tinyxml2::XMLElement*, Type, int* = nullptr);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent, Type type, int id = -1);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, Type, int = -1);
|
||||||
int length(Type type);
|
int length(Type);
|
||||||
Frame frame_generate(float time, Type type);
|
Frame frame_generate(float, Type);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Animation
|
class Animation
|
||||||
@@ -194,10 +203,12 @@ namespace anm2ed::anm2
|
|||||||
Item triggers;
|
Item triggers;
|
||||||
|
|
||||||
Animation();
|
Animation();
|
||||||
Animation(tinyxml2::XMLElement* element);
|
Animation(tinyxml2::XMLElement*);
|
||||||
Item* item_get(Type type, int id = -1);
|
Item* item_get(Type, int = -1);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
|
void item_remove(Type, int = -1);
|
||||||
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*);
|
||||||
int length();
|
int length();
|
||||||
|
std::string to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Animations
|
struct Animations
|
||||||
@@ -207,42 +218,41 @@ namespace anm2ed::anm2
|
|||||||
|
|
||||||
Animations();
|
Animations();
|
||||||
|
|
||||||
Animations(tinyxml2::XMLElement* element);
|
Animations(tinyxml2::XMLElement*);
|
||||||
void serialize(tinyxml2::XMLDocument& document, tinyxml2::XMLElement* parent);
|
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*);
|
||||||
int length();
|
int length();
|
||||||
int merge(int target, std::set<int>& sources, types::merge::Type type, bool isDeleteAfter = true);
|
int merge(int, std::set<int>&, types::merge::Type = types::merge::APPEND, bool = true);
|
||||||
|
bool animations_deserialize(const std::string&, int, std::set<int>&, std::string* = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Anm2
|
class Anm2
|
||||||
{
|
{
|
||||||
bool isValid{false};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Info info{};
|
Info info{};
|
||||||
Content content{};
|
Content content{};
|
||||||
Animations animations{};
|
Animations animations{};
|
||||||
|
|
||||||
Anm2();
|
Anm2();
|
||||||
bool serialize(const std::string& path, std::string* errorString = nullptr);
|
bool serialize(const std::string&, std::string* = nullptr);
|
||||||
std::string to_string();
|
std::string to_string();
|
||||||
Anm2(const std::string& path, std::string* errorString = nullptr);
|
Anm2(const std::string&, std::string* = nullptr);
|
||||||
uint64_t hash();
|
uint64_t hash();
|
||||||
Animation* animation_get(Reference& reference);
|
Animation* animation_get(Reference&);
|
||||||
Item* item_get(Reference& reference);
|
Item* item_get(Reference&);
|
||||||
Frame* frame_get(Reference& reference);
|
Frame* frame_get(Reference&);
|
||||||
bool spritesheet_add(const std::string& directory, const std::string& path, int& id);
|
bool spritesheet_add(const std::string&, const std::string&, int&);
|
||||||
Spritesheet* spritesheet_get(int id);
|
Spritesheet* spritesheet_get(int);
|
||||||
void spritesheet_remove(int id);
|
void spritesheet_remove(int);
|
||||||
std::set<int> spritesheets_unused();
|
std::set<int> spritesheets_unused();
|
||||||
int layer_add();
|
int layer_add();
|
||||||
Reference layer_add(Reference reference = REFERENCE_DEFAULT, std::string name = {}, int spritesheetID = 0,
|
Reference layer_add(Reference = REFERENCE_DEFAULT, std::string = {}, int = 0,
|
||||||
types::locale::Type locale = types::locale::GLOBAL);
|
types::locale::Type = types::locale::GLOBAL);
|
||||||
Reference null_add(Reference reference = REFERENCE_DEFAULT, std::string name = {},
|
Reference null_add(Reference = REFERENCE_DEFAULT, std::string = {}, types::locale::Type = types::locale::GLOBAL);
|
||||||
types::locale::Type locale = types::locale::GLOBAL);
|
void event_add(int&);
|
||||||
void event_add(int& id);
|
std::set<int> events_unused(Reference = REFERENCE_DEFAULT);
|
||||||
std::set<int> events_unused(Reference reference = REFERENCE_DEFAULT);
|
std::set<int> layers_unused(Reference = REFERENCE_DEFAULT);
|
||||||
std::set<int> layers_unused(Reference reference = REFERENCE_DEFAULT);
|
std::set<int> nulls_unused(Reference = REFERENCE_DEFAULT);
|
||||||
std::set<int> nulls_unused(Reference reference = REFERENCE_DEFAULT);
|
|
||||||
std::vector<std::string> spritesheet_names_get();
|
std::vector<std::string> spritesheet_names_get();
|
||||||
|
void bake(Reference, int = 1, bool = true, bool = true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
24
src/canvas.h
24
src/canvas.h
@@ -32,24 +32,24 @@ namespace anm2ed::canvas
|
|||||||
glm::vec2 size{};
|
glm::vec2 size{};
|
||||||
|
|
||||||
Canvas();
|
Canvas();
|
||||||
Canvas(glm::vec2 size);
|
Canvas(glm::vec2);
|
||||||
~Canvas();
|
~Canvas();
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
void framebuffer_set();
|
void framebuffer_set();
|
||||||
void framebuffer_resize_check();
|
void framebuffer_resize_check();
|
||||||
void size_set(glm::vec2 size);
|
void size_set(glm::vec2);
|
||||||
glm::mat4 transform_get(float zoom, glm::vec2 pan);
|
glm::mat4 transform_get(float, glm::vec2);
|
||||||
void axes_render(shader::Shader& shader, float zoom, glm::vec2 pan, glm::vec4 color = glm::vec4(1.0f));
|
void axes_render(shader::Shader&, float, glm::vec2, glm::vec4 = glm::vec4(1.0f));
|
||||||
void grid_render(shader::Shader& shader, float zoom, glm::vec2 pan, glm::ivec2 size = glm::ivec2(32, 32),
|
void grid_render(shader::Shader&, float, glm::vec2, glm::ivec2 = glm::ivec2(32, 32), glm::ivec2 = {},
|
||||||
glm::ivec2 offset = {}, glm::vec4 color = glm::vec4(1.0f));
|
glm::vec4 = glm::vec4(1.0f));
|
||||||
void texture_render(shader::Shader& shader, GLuint& texture, glm::mat4& transform, glm::vec4 tint = glm::vec4(1.0f),
|
void texture_render(shader::Shader&, GLuint&, glm::mat4&, glm::vec4 = glm::vec4(1.0f), glm::vec3 = {},
|
||||||
glm::vec3 colorOffset = {}, float* vertices = (float*)TEXTURE_VERTICES);
|
float* = (float*)TEXTURE_VERTICES);
|
||||||
void rect_render(shader::Shader& shader, glm::mat4& transform, glm::vec4 color = glm::vec4(1.0f));
|
void rect_render(shader::Shader&, glm::mat4&, glm::vec4 = glm::vec4(1.0f));
|
||||||
void viewport_set();
|
void viewport_set();
|
||||||
void clear(glm::vec4& color);
|
void clear(glm::vec4&);
|
||||||
void bind();
|
void bind();
|
||||||
void unbind();
|
void unbind();
|
||||||
void zoom_set(float& zoom, glm::vec2& pan, glm::vec2& focus, float step);
|
void zoom_set(float&, glm::vec2&, glm::vec2&, float);
|
||||||
glm::vec2 position_translate(float& zoom, glm::vec2& pan, glm::vec2 position);
|
glm::vec2 position_translate(float&, glm::vec2&, glm::vec2);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/clipboard.cpp
Normal file
25
src/clipboard.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "clipboard.h"
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
namespace anm2ed::clipboard
|
||||||
|
{
|
||||||
|
std::string Clipboard::get()
|
||||||
|
{
|
||||||
|
auto text = SDL_GetClipboardText();
|
||||||
|
auto string = std::string(text);
|
||||||
|
SDL_free(text);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Clipboard::is_empty()
|
||||||
|
{
|
||||||
|
return get().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clipboard::set(const std::string& string)
|
||||||
|
{
|
||||||
|
SDL_SetClipboardText(string.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/clipboard.h
Normal file
14
src/clipboard.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace anm2ed::clipboard
|
||||||
|
{
|
||||||
|
class Clipboard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool is_empty();
|
||||||
|
std::string get();
|
||||||
|
void set(const std::string&);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -26,14 +26,14 @@ namespace anm2ed::dialog
|
|||||||
int replaceID{-1};
|
int replaceID{-1};
|
||||||
|
|
||||||
Dialog();
|
Dialog();
|
||||||
Dialog(SDL_Window* window);
|
Dialog(SDL_Window*);
|
||||||
void anm2_new();
|
void anm2_new();
|
||||||
void anm2_open();
|
void anm2_open();
|
||||||
void anm2_save();
|
void anm2_save();
|
||||||
void spritesheet_open();
|
void spritesheet_open();
|
||||||
void spritesheet_replace();
|
void spritesheet_replace();
|
||||||
void file_explorer_open(const std::string& path);
|
void file_explorer_open(const std::string&);
|
||||||
void reset();
|
void reset();
|
||||||
bool is_selected_file(Type type);
|
bool is_selected_file(Type);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
using namespace anm2ed::animations;
|
using namespace anm2ed::animations;
|
||||||
using namespace anm2ed::dialog;
|
using namespace anm2ed::dialog;
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::documents;
|
using namespace anm2ed::documents;
|
||||||
using namespace anm2ed::playback;
|
using namespace anm2ed::playback;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
@@ -15,8 +16,8 @@ using namespace anm2ed::taskbar;
|
|||||||
|
|
||||||
namespace anm2ed::dockspace
|
namespace anm2ed::dockspace
|
||||||
{
|
{
|
||||||
void Dockspace::update(Taskbar& taskbar, Documents& documents, DocumentManager& manager, Settings& settings,
|
void Dockspace::update(Taskbar& taskbar, Documents& documents, Manager& manager, Settings& settings,
|
||||||
Resources& resources, Dialog& dialog, Playback& playback)
|
Resources& resources, Dialog& dialog, Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto viewport = ImGui::GetMainViewport();
|
auto viewport = ImGui::GetMainViewport();
|
||||||
@@ -35,17 +36,17 @@ namespace anm2ed::dockspace
|
|||||||
{
|
{
|
||||||
if (auto document = manager.get(); document)
|
if (auto document = manager.get(); document)
|
||||||
{
|
{
|
||||||
if (settings.windowIsAnimationPreview) animationPreview.update(manager, settings, resources, playback);
|
if (settings.windowIsAnimationPreview) animationPreview.update(manager, settings, resources);
|
||||||
if (settings.windowIsAnimations) animations.update(*document, manager.selected, settings, resources);
|
if (settings.windowIsAnimations) animations.update(manager, settings, resources, clipboard);
|
||||||
if (settings.windowIsEvents) events.update(manager, settings, resources);
|
if (settings.windowIsEvents) events.update(manager, settings, resources, clipboard);
|
||||||
if (settings.windowIsFrameProperties) frameProperties.update(manager, settings);
|
if (settings.windowIsFrameProperties) frameProperties.update(manager, settings);
|
||||||
if (settings.windowIsLayers) layers.update(*document, settings, resources);
|
if (settings.windowIsLayers) layers.update(manager, settings, resources, clipboard);
|
||||||
if (settings.windowIsNulls) nulls.update(*document, manager.selected, settings, resources);
|
if (settings.windowIsNulls) nulls.update(manager, settings, resources, clipboard);
|
||||||
if (settings.windowIsOnionskin) onionskin.update(settings);
|
if (settings.windowIsOnionskin) onionskin.update(settings);
|
||||||
if (settings.windowIsSpritesheetEditor) spritesheetEditor.update(manager, settings, resources);
|
if (settings.windowIsSpritesheetEditor) spritesheetEditor.update(manager, settings, resources);
|
||||||
if (settings.windowIsSpritesheets) spritesheets.update(*document, settings, resources, dialog);
|
if (settings.windowIsSpritesheets) spritesheets.update(manager, settings, resources, dialog, clipboard);
|
||||||
if (settings.windowIsTimeline) timeline.update(manager, settings, resources, playback);
|
if (settings.windowIsTimeline) timeline.update(manager, settings, resources);
|
||||||
if (settings.windowIsTools) tools.update(settings, resources);
|
if (settings.windowIsTools) tools.update(manager, settings, resources);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ namespace anm2ed::dockspace
|
|||||||
tools::Tools tools;
|
tools::Tools tools;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(taskbar::Taskbar& taskbar, documents::Documents& documents, document_manager::DocumentManager& manager,
|
void update(taskbar::Taskbar&, documents::Documents&, manager::Manager&, settings::Settings&, resources::Resources&,
|
||||||
settings::Settings& settings, resources::Resources& resources, dialog::Dialog& dialog,
|
dialog::Dialog&, clipboard::Clipboard&);
|
||||||
playback::Playback& playback);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
423
src/document.cpp
423
src/document.cpp
@@ -2,24 +2,21 @@
|
|||||||
|
|
||||||
#include "anm2.h"
|
#include "anm2.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
#include "toast.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
using namespace anm2ed::anm2;
|
using namespace anm2ed::anm2;
|
||||||
using namespace anm2ed::filesystem;
|
using namespace anm2ed::filesystem;
|
||||||
|
using namespace anm2ed::toast;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
|
using namespace anm2ed::util;
|
||||||
|
|
||||||
namespace anm2ed::document
|
namespace anm2ed::document
|
||||||
{
|
{
|
||||||
Document::Document()
|
|
||||||
{
|
|
||||||
for (auto& value : isJustChanged)
|
|
||||||
value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Document::Document(const std::string& path, bool isNew, std::string* errorString)
|
Document::Document(const std::string& path, bool isNew, std::string* errorString)
|
||||||
{
|
{
|
||||||
for (auto& value : isJustChanged)
|
|
||||||
value = true;
|
|
||||||
|
|
||||||
if (!path_is_exist(path)) return;
|
if (!path_is_exist(path)) return;
|
||||||
|
|
||||||
if (isNew)
|
if (isNew)
|
||||||
@@ -31,8 +28,8 @@ namespace anm2ed::document
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->path = path;
|
this->path = path;
|
||||||
on_change();
|
|
||||||
clean();
|
clean();
|
||||||
|
change(change::ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::save(const std::string& path, std::string* errorString)
|
bool Document::save(const std::string& path, std::string* errorString)
|
||||||
@@ -62,7 +59,42 @@ namespace anm2ed::document
|
|||||||
void Document::change(change::Type type)
|
void Document::change(change::Type type)
|
||||||
{
|
{
|
||||||
hash_set();
|
hash_set();
|
||||||
isJustChanged[type] = true;
|
|
||||||
|
auto layer_set = [&]() { unusedLayerIDs = anm2.layers_unused(); };
|
||||||
|
auto null_set = [&]() { unusedNullIDs = anm2.nulls_unused(); };
|
||||||
|
auto event_set = [&]() { unusedEventIDs = anm2.events_unused(); };
|
||||||
|
auto spritesheet_set = [&]()
|
||||||
|
{
|
||||||
|
unusedSpritesheetIDs = anm2.spritesheets_unused();
|
||||||
|
spritesheetNames = anm2.spritesheet_names_get();
|
||||||
|
spritesheetNamesCstr.clear();
|
||||||
|
for (auto& name : spritesheetNames)
|
||||||
|
spritesheetNamesCstr.push_back(name.c_str());
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case change::LAYERS:
|
||||||
|
layer_set();
|
||||||
|
break;
|
||||||
|
case change::NULLS:
|
||||||
|
null_set();
|
||||||
|
break;
|
||||||
|
case change::EVENTS:
|
||||||
|
event_set();
|
||||||
|
break;
|
||||||
|
case change::SPRITESHEETS:
|
||||||
|
spritesheet_set();
|
||||||
|
break;
|
||||||
|
case change::ALL:
|
||||||
|
layer_set();
|
||||||
|
null_set();
|
||||||
|
event_set();
|
||||||
|
spritesheet_set();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::is_dirty()
|
bool Document::is_dirty()
|
||||||
@@ -70,11 +102,6 @@ namespace anm2ed::document
|
|||||||
return hash != saveHash;
|
return hash != saveHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::is_just_changed(types::change::Type type)
|
|
||||||
{
|
|
||||||
return isJustChanged[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Document::directory_get()
|
std::string Document::directory_get()
|
||||||
{
|
{
|
||||||
return path.parent_path();
|
return path.parent_path();
|
||||||
@@ -85,9 +112,9 @@ namespace anm2ed::document
|
|||||||
return path.filename().string();
|
return path.filename().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
anm2::Animation* Document::animation_get()
|
bool Document::is_valid()
|
||||||
{
|
{
|
||||||
return anm2.animation_get(reference);
|
return !path.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
anm2::Frame* Document::frame_get()
|
anm2::Frame* Document::frame_get()
|
||||||
@@ -95,6 +122,42 @@ namespace anm2ed::document
|
|||||||
return anm2.frame_get(reference);
|
return anm2.frame_get(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Document::frames_bake(int interval, bool isRoundScale, bool isRoundRotation)
|
||||||
|
{
|
||||||
|
snapshot("Bake Frames");
|
||||||
|
anm2.bake(reference, interval, isRoundScale, isRoundRotation);
|
||||||
|
change(change::FRAMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::frames_add(anm2::Item* item)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
auto frame = frame_get();
|
||||||
|
|
||||||
|
if (frame)
|
||||||
|
{
|
||||||
|
item->frames.insert(item->frames.begin() + reference.frameIndex, *frame);
|
||||||
|
reference.frameIndex++;
|
||||||
|
}
|
||||||
|
else if (!item->frames.empty())
|
||||||
|
{
|
||||||
|
auto frame = item->frames.back();
|
||||||
|
item->frames.emplace_back(frame);
|
||||||
|
reference.frameIndex = item->frames.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::frames_delete(anm2::Item* item)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
snapshot("Delete Frames");
|
||||||
|
item->frames.erase(item->frames.begin() + reference.frameIndex);
|
||||||
|
reference.frameIndex = glm::max(-1, --reference.frameIndex);
|
||||||
|
change(change::FRAMES);
|
||||||
|
}
|
||||||
|
|
||||||
anm2::Item* Document::item_get()
|
anm2::Item* Document::item_get()
|
||||||
{
|
{
|
||||||
return anm2.item_get(reference);
|
return anm2.item_get(reference);
|
||||||
@@ -105,27 +168,317 @@ namespace anm2ed::document
|
|||||||
return anm2.spritesheet_get(referenceSpritesheet);
|
return anm2.spritesheet_get(referenceSpritesheet);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Document::is_valid()
|
void Document::spritesheet_add(const std::string& path)
|
||||||
{
|
{
|
||||||
return !path.empty();
|
int id{};
|
||||||
}
|
snapshot("Add Spritesheet");
|
||||||
|
if (anm2.spritesheet_add(directory_get(), path, id))
|
||||||
void Document::on_change()
|
|
||||||
{
|
|
||||||
if (is_just_changed(change::SPRITESHEETS))
|
|
||||||
{
|
{
|
||||||
spritesheetNames = anm2.spritesheet_names_get();
|
spritesheetMultiSelect = {id};
|
||||||
spritesheetNamesCstr.clear();
|
toasts.info(std::format("Initialized spritesheet #{}: {}", id, path));
|
||||||
for (auto& name : spritesheetNames)
|
change(change::SPRITESHEETS);
|
||||||
spritesheetNamesCstr.push_back(name.c_str());
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to initialize spritesheet: {}", path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Document::update()
|
void Document::spritesheets_deserialize(const std::string& string, merge::Type type)
|
||||||
{
|
{
|
||||||
on_change();
|
snapshot("Paste Spritesheet(s)");
|
||||||
|
std::string errorString{};
|
||||||
for (auto& value : isJustChanged)
|
if (anm2.content.spritesheets_deserialize(string, directory_get(), type, &errorString))
|
||||||
value = false;
|
change(change::SPRITESHEETS);
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to deserialize spritesheet(s): {}", errorString));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
void Document::layers_deserialize(const std::string& string, merge::Type type)
|
||||||
|
{
|
||||||
|
snapshot("Paste Layer(s)");
|
||||||
|
std::string errorString{};
|
||||||
|
if (anm2.content.layers_deserialize(string, type, &errorString))
|
||||||
|
change(change::NULLS);
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to deserialize layer(s): {}", errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::layer_set(anm2::Layer& layer)
|
||||||
|
{
|
||||||
|
if (referenceLayer > -1)
|
||||||
|
{
|
||||||
|
snapshot("Set Layer");
|
||||||
|
anm2.content.layers[referenceLayer] = layer;
|
||||||
|
layersMultiSelect = {referenceLayer};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snapshot("Add Layer");
|
||||||
|
auto id = map::next_id_get(anm2.content.layers);
|
||||||
|
anm2.content.layers[id] = layer;
|
||||||
|
layersMultiSelect = {id};
|
||||||
|
}
|
||||||
|
change(change::LAYERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::layers_remove_unused()
|
||||||
|
{
|
||||||
|
snapshot("Remove Unused Layers");
|
||||||
|
for (auto& id : unusedLayerIDs)
|
||||||
|
anm2.content.layers.erase(id);
|
||||||
|
change(change::LAYERS);
|
||||||
|
unusedLayerIDs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::null_set(anm2::Null& null)
|
||||||
|
{
|
||||||
|
if (referenceNull > -1)
|
||||||
|
{
|
||||||
|
snapshot("Set Null");
|
||||||
|
anm2.content.nulls[referenceNull] = null;
|
||||||
|
nullMultiSelect = {referenceNull};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snapshot("Add Null");
|
||||||
|
auto id = map::next_id_get(anm2.content.nulls);
|
||||||
|
anm2.content.nulls[id] = null;
|
||||||
|
nullMultiSelect = {id};
|
||||||
|
}
|
||||||
|
change(change::NULLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::null_rect_toggle(anm2::Null& null)
|
||||||
|
{
|
||||||
|
snapshot("Null Rect");
|
||||||
|
null.isShowRect = !null.isShowRect;
|
||||||
|
change(change::NULLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::nulls_remove_unused()
|
||||||
|
{
|
||||||
|
snapshot("Remove Unused Nulls");
|
||||||
|
for (auto& id : unusedNullIDs)
|
||||||
|
anm2.content.nulls.erase(id);
|
||||||
|
change(change::NULLS);
|
||||||
|
unusedNullIDs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::nulls_deserialize(const std::string& string, merge::Type type)
|
||||||
|
{
|
||||||
|
snapshot("Paste Null(s)");
|
||||||
|
std::string errorString{};
|
||||||
|
if (anm2.content.nulls_deserialize(string, type, &errorString))
|
||||||
|
change(change::NULLS);
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to deserialize null(s): {}", errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::event_add()
|
||||||
|
{
|
||||||
|
snapshot("Add Event");
|
||||||
|
int id{};
|
||||||
|
anm2.event_add(id);
|
||||||
|
eventMultiSelect = {id};
|
||||||
|
change(change::EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::events_remove_unused()
|
||||||
|
{
|
||||||
|
snapshot("Remove Unused Events");
|
||||||
|
for (auto& id : unusedEventIDs)
|
||||||
|
anm2.content.events.erase(id);
|
||||||
|
change(change::EVENTS);
|
||||||
|
unusedEventIDs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::events_deserialize(const std::string& string, merge::Type type)
|
||||||
|
{
|
||||||
|
snapshot("Paste Event(s)");
|
||||||
|
std::string errorString{};
|
||||||
|
if (anm2.content.events_deserialize(string, type, &errorString))
|
||||||
|
change(change::EVENTS);
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to deserialize event(s): {}", errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::item_visible_toggle(anm2::Item* item)
|
||||||
|
{
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
snapshot("Item Visible");
|
||||||
|
item->isVisible = !item->isVisible;
|
||||||
|
|
||||||
|
change(change::ITEMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::item_add(anm2::Type type, int id, std::string& name, locale::Type locale, int spritesheetID)
|
||||||
|
{
|
||||||
|
snapshot("Add Item");
|
||||||
|
|
||||||
|
anm2::Reference addReference;
|
||||||
|
|
||||||
|
if (type == anm2::LAYER)
|
||||||
|
addReference =
|
||||||
|
anm2.layer_add({reference.animationIndex, anm2::LAYER, id}, name, spritesheetID, (locale::Type)locale);
|
||||||
|
else if (type == anm2::NULL_)
|
||||||
|
addReference = anm2.null_add({reference.animationIndex, anm2::LAYER, id}, name, (locale::Type)locale);
|
||||||
|
|
||||||
|
reference = addReference;
|
||||||
|
|
||||||
|
change(change::ITEMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::item_remove(anm2::Animation* animation)
|
||||||
|
{
|
||||||
|
snapshot("Remove Item");
|
||||||
|
|
||||||
|
if (!animation) return;
|
||||||
|
|
||||||
|
animation->item_remove(reference.itemType, reference.itemID);
|
||||||
|
reference = {reference.animationIndex};
|
||||||
|
change(change::ITEMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
anm2::Animation* Document::animation_get()
|
||||||
|
{
|
||||||
|
return anm2.animation_get(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animation_add()
|
||||||
|
{
|
||||||
|
snapshot("Add Animation");
|
||||||
|
anm2::Animation animation;
|
||||||
|
if (anm2::Animation* referenceAnimation = animation_get())
|
||||||
|
{
|
||||||
|
for (auto [id, layerAnimation] : referenceAnimation->layerAnimations)
|
||||||
|
animation.layerAnimations[id] = anm2::Item();
|
||||||
|
animation.layerOrder = referenceAnimation->layerOrder;
|
||||||
|
for (auto [id, nullAnimation] : referenceAnimation->nullAnimations)
|
||||||
|
animation.nullAnimations[id] = anm2::Item();
|
||||||
|
}
|
||||||
|
animation.rootAnimation.frames.emplace_back(anm2::Frame());
|
||||||
|
|
||||||
|
auto index =
|
||||||
|
animationMultiSelect.empty() ? (int)anm2.animations.items.size() - 1 : *animationMultiSelect.rbegin() + 1;
|
||||||
|
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
|
||||||
|
animationMultiSelect = {index};
|
||||||
|
reference = {index};
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animation_duplicate()
|
||||||
|
{
|
||||||
|
snapshot("Duplicate Animation(s)");
|
||||||
|
auto duplicated = animationMultiSelect;
|
||||||
|
auto duplicatedEnd = std::ranges::max(duplicated);
|
||||||
|
for (auto& id : duplicated)
|
||||||
|
{
|
||||||
|
anm2.animations.items.insert(anm2.animations.items.begin() + duplicatedEnd, anm2.animations.items[id]);
|
||||||
|
animationMultiSelect.insert(++duplicatedEnd);
|
||||||
|
animationMultiSelect.erase(id);
|
||||||
|
}
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animations_move(std::vector<int>& indices, int index)
|
||||||
|
{
|
||||||
|
snapshot("Move Animation(s)");
|
||||||
|
animationMultiSelect = vector::move_indices(anm2.animations.items, indices, index);
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animation_remove()
|
||||||
|
{
|
||||||
|
snapshot("Remove Animation(s)");
|
||||||
|
for (auto& i : animationMultiSelect | std::views::reverse)
|
||||||
|
anm2.animations.items.erase(anm2.animations.items.begin() + i);
|
||||||
|
animationMultiSelect.clear();
|
||||||
|
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animation_default()
|
||||||
|
{
|
||||||
|
snapshot("Default Animation");
|
||||||
|
anm2.animations.defaultAnimation = anm2.animations.items[*animationMultiSelect.begin()].name;
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animations_deserialize(const std::string& string)
|
||||||
|
{
|
||||||
|
snapshot("Paste Animations");
|
||||||
|
auto& multiSelect = animationMultiSelect;
|
||||||
|
auto start = multiSelect.empty() ? anm2.animations.items.size() : *multiSelect.rbegin() + 1;
|
||||||
|
std::set<int> indices{};
|
||||||
|
std::string errorString{};
|
||||||
|
if (anm2.animations.animations_deserialize(string, start, indices, &errorString))
|
||||||
|
{
|
||||||
|
multiSelect = indices;
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to deserialize animation(s): {}", errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animations_merge_quick()
|
||||||
|
{
|
||||||
|
snapshot("Merge Animations");
|
||||||
|
int merged{};
|
||||||
|
if (animationMultiSelect.size() > 1)
|
||||||
|
merged = anm2.animations.merge(*animationMultiSelect.begin(), animationMultiSelect);
|
||||||
|
else if (animationMultiSelect.size() == 1 && *animationMultiSelect.begin() != (int)anm2.animations.items.size() - 1)
|
||||||
|
{
|
||||||
|
auto start = *animationMultiSelect.begin();
|
||||||
|
auto next = *animationMultiSelect.begin() + 1;
|
||||||
|
std::set<int> animationSet{};
|
||||||
|
animationSet.insert(start);
|
||||||
|
animationSet.insert(next);
|
||||||
|
|
||||||
|
merged = anm2.animations.merge(start, animationSet);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
animationMultiSelect = {merged};
|
||||||
|
reference = {merged};
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::animations_merge(merge::Type type, bool isDeleteAnimationsAfter)
|
||||||
|
{
|
||||||
|
snapshot("Merge Animations");
|
||||||
|
auto merged = anm2.animations.merge(mergeTarget, animationMergeMultiSelect, type, isDeleteAnimationsAfter);
|
||||||
|
animationMultiSelect = {merged};
|
||||||
|
reference = {merged};
|
||||||
|
change(change::ANIMATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::snapshot(const std::string& message)
|
||||||
|
{
|
||||||
|
snapshots.push(anm2, reference, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::undo()
|
||||||
|
{
|
||||||
|
snapshots.undo(anm2, reference, message);
|
||||||
|
toasts.info(std::format("Undo: {}", message));
|
||||||
|
change(change::ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Document::redo()
|
||||||
|
{
|
||||||
|
toasts.info(std::format("Redo: {}", message));
|
||||||
|
snapshots.redo(anm2, reference, message);
|
||||||
|
change(change::ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::is_undo()
|
||||||
|
{
|
||||||
|
return !snapshots.undoStack.is_empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Document::is_redo()
|
||||||
|
{
|
||||||
|
return !snapshots.redoStack.is_empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/document.h
101
src/document.h
@@ -4,6 +4,9 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "anm2.h"
|
#include "anm2.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "playback.h"
|
||||||
|
#include "snapshots.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
@@ -15,7 +18,9 @@ namespace anm2ed::document
|
|||||||
public:
|
public:
|
||||||
std::filesystem::path path{};
|
std::filesystem::path path{};
|
||||||
anm2::Anm2 anm2{};
|
anm2::Anm2 anm2{};
|
||||||
anm2::Reference reference{};
|
std::string message{};
|
||||||
|
playback::Playback playback{};
|
||||||
|
snapshots::Snapshots snapshots{};
|
||||||
|
|
||||||
float previewZoom{200};
|
float previewZoom{200};
|
||||||
glm::vec2 previewPan{};
|
glm::vec2 previewPan{};
|
||||||
@@ -23,40 +28,92 @@ namespace anm2ed::document
|
|||||||
float editorZoom{200};
|
float editorZoom{200};
|
||||||
int overlayIndex{};
|
int overlayIndex{};
|
||||||
|
|
||||||
|
anm2::Reference reference{};
|
||||||
|
int hoveredAnimation{-1};
|
||||||
|
int mergeTarget{-1};
|
||||||
|
imgui::MultiSelectStorage animationMultiSelect;
|
||||||
|
imgui::MultiSelectStorage animationMergeMultiSelect;
|
||||||
|
|
||||||
int referenceSpritesheet{-1};
|
int referenceSpritesheet{-1};
|
||||||
int referenceLayer{-1};
|
int hoveredSpritesheet{-1};
|
||||||
|
std::set<int> unusedSpritesheetIDs{};
|
||||||
std::set<int> selectedEvents{};
|
|
||||||
std::set<int> selectedLayers{};
|
|
||||||
std::set<int> selectedNulls{};
|
|
||||||
std::set<int> selectedAnimations{};
|
|
||||||
std::set<int> selectedSpritesheets{};
|
|
||||||
|
|
||||||
std::vector<std::string> spritesheetNames{};
|
std::vector<std::string> spritesheetNames{};
|
||||||
std::vector<const char*> spritesheetNamesCstr{};
|
std::vector<const char*> spritesheetNamesCstr{};
|
||||||
|
imgui::MultiSelectStorage spritesheetMultiSelect;
|
||||||
|
|
||||||
|
int referenceLayer{-1};
|
||||||
|
int hoveredLayer{-1};
|
||||||
|
std::set<int> unusedLayerIDs{};
|
||||||
|
imgui::MultiSelectStorage layersMultiSelect;
|
||||||
|
|
||||||
|
int referenceNull{-1};
|
||||||
|
int hoveredNull{-1};
|
||||||
|
std::set<int> unusedNullIDs{};
|
||||||
|
imgui::MultiSelectStorage nullMultiSelect;
|
||||||
|
|
||||||
|
int referenceEvent{-1};
|
||||||
|
int hoveredEvent{-1};
|
||||||
|
std::set<int> unusedEventIDs{};
|
||||||
|
imgui::MultiSelectStorage eventMultiSelect;
|
||||||
|
|
||||||
uint64_t hash{};
|
uint64_t hash{};
|
||||||
uint64_t saveHash{};
|
uint64_t saveHash{};
|
||||||
bool isJustChanged[types::change::COUNT]{};
|
|
||||||
bool isOpen{true};
|
bool isOpen{true};
|
||||||
|
|
||||||
Document();
|
Document(const std::string&, bool = false, std::string* = nullptr);
|
||||||
|
bool save(const std::string& = {}, std::string* = nullptr);
|
||||||
Document(const std::string& path, bool isNew = false, std::string* errorString = nullptr);
|
|
||||||
bool save(const std::string& path = {}, std::string* errorString = nullptr);
|
|
||||||
void hash_set();
|
void hash_set();
|
||||||
void clean();
|
void clean();
|
||||||
void on_change();
|
void on_change();
|
||||||
void change(types::change::Type type);
|
void change(types::change::Type);
|
||||||
bool is_just_changed(types::change::Type type);
|
|
||||||
bool is_dirty();
|
bool is_dirty();
|
||||||
void update();
|
|
||||||
std::string directory_get();
|
std::string directory_get();
|
||||||
std::string filename_get();
|
std::string filename_get();
|
||||||
anm2::Animation* animation_get();
|
|
||||||
anm2::Frame* frame_get();
|
|
||||||
anm2::Item* item_get();
|
|
||||||
anm2::Spritesheet* spritesheet_get();
|
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
|
|
||||||
|
anm2::Frame* frame_get();
|
||||||
|
void frames_add(anm2::Item* item);
|
||||||
|
void frames_delete(anm2::Item* item);
|
||||||
|
void frames_bake(int, bool, bool);
|
||||||
|
|
||||||
|
anm2::Item* item_get();
|
||||||
|
void item_add(anm2::Type, int, std::string&, types::locale::Type, int);
|
||||||
|
void item_remove(anm2::Animation* animation);
|
||||||
|
|
||||||
|
anm2::Spritesheet* spritesheet_get();
|
||||||
|
void spritesheet_add(const std::string&);
|
||||||
|
void spritesheets_deserialize(const std::string&, types::merge::Type);
|
||||||
|
|
||||||
|
void layer_set(anm2::Layer& layer);
|
||||||
|
void layers_remove_unused();
|
||||||
|
void layers_deserialize(const std::string&, types::merge::Type);
|
||||||
|
|
||||||
|
void null_set(anm2::Null& null);
|
||||||
|
void null_rect_toggle(anm2::Null& null);
|
||||||
|
void nulls_remove_unused();
|
||||||
|
void nulls_deserialize(const std::string&, types::merge::Type);
|
||||||
|
|
||||||
|
void event_add();
|
||||||
|
void events_remove_unused();
|
||||||
|
void events_deserialize(const std::string&, types::merge::Type);
|
||||||
|
|
||||||
|
void item_visible_toggle(anm2::Item*);
|
||||||
|
|
||||||
|
void animation_add();
|
||||||
|
void animation_duplicate();
|
||||||
|
void animation_default();
|
||||||
|
void animation_remove();
|
||||||
|
void animations_move(std::vector<int>&, int);
|
||||||
|
void animations_merge(types::merge::Type, bool);
|
||||||
|
void animations_merge_quick();
|
||||||
|
anm2::Animation* animation_get();
|
||||||
|
void animations_deserialize(const std::string& string);
|
||||||
|
|
||||||
|
void snapshot(const std::string& message);
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
|
||||||
|
bool is_undo();
|
||||||
|
bool is_redo();
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
#include "document_manager.h"
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
using namespace anm2ed::util;
|
|
||||||
|
|
||||||
namespace anm2ed::document_manager
|
|
||||||
{
|
|
||||||
Document* DocumentManager::get()
|
|
||||||
{
|
|
||||||
return vector::find(documents, selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
Document* DocumentManager::get(int index)
|
|
||||||
{
|
|
||||||
return vector::find(documents, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DocumentManager::open(const std::string& path, bool isNew)
|
|
||||||
{
|
|
||||||
std::string errorString{};
|
|
||||||
Document document = Document(path, isNew, &errorString);
|
|
||||||
if (document.is_valid())
|
|
||||||
{
|
|
||||||
documents.emplace_back(std::move(document));
|
|
||||||
selected = documents.size() - 1;
|
|
||||||
pendingSelected = selected;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DocumentManager::new_(const std::string& path)
|
|
||||||
{
|
|
||||||
return open(path, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DocumentManager::save(int index, const std::string& path)
|
|
||||||
{
|
|
||||||
auto document = get(index);
|
|
||||||
if (!document) return;
|
|
||||||
std::string errorString{};
|
|
||||||
|
|
||||||
document->path = !path.empty() ? path : document->path.string();
|
|
||||||
|
|
||||||
document->save(document->path, &errorString);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DocumentManager::save(const std::string& path)
|
|
||||||
{
|
|
||||||
save(selected, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DocumentManager::close(int index)
|
|
||||||
{
|
|
||||||
documents.erase(documents.begin() + index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "document.h"
|
|
||||||
|
|
||||||
using namespace anm2ed::document;
|
|
||||||
|
|
||||||
namespace anm2ed::document_manager
|
|
||||||
{
|
|
||||||
class DocumentManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::vector<Document> documents{};
|
|
||||||
int selected{};
|
|
||||||
int pendingSelected{};
|
|
||||||
|
|
||||||
Document* get();
|
|
||||||
Document* get(int index);
|
|
||||||
bool open(const std::string& path, bool isNew = false);
|
|
||||||
bool new_(const std::string& path);
|
|
||||||
void save(int index, const std::string& path = {});
|
|
||||||
void save(const std::string& path = {});
|
|
||||||
void close(int index);
|
|
||||||
void spritesheet_add(const std::string& path);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -5,12 +5,12 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
using namespace anm2ed::taskbar;
|
using namespace anm2ed::taskbar;
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
|
|
||||||
namespace anm2ed::documents
|
namespace anm2ed::documents
|
||||||
{
|
{
|
||||||
void Documents::update(Taskbar& taskbar, DocumentManager& manager, Resources& resources)
|
void Documents::update(Taskbar& taskbar, Manager& manager, Resources& resources)
|
||||||
{
|
{
|
||||||
auto viewport = ImGui::GetMainViewport();
|
auto viewport = ImGui::GetMainViewport();
|
||||||
auto windowHeight = ImGui::GetFrameHeightWithSpacing();
|
auto windowHeight = ImGui::GetFrameHeightWithSpacing();
|
||||||
@@ -32,7 +32,6 @@ namespace anm2ed::documents
|
|||||||
for (auto [i, document] : std::views::enumerate(manager.documents))
|
for (auto [i, document] : std::views::enumerate(manager.documents))
|
||||||
{
|
{
|
||||||
auto isDirty = document.is_dirty();
|
auto isDirty = document.is_dirty();
|
||||||
auto isSelected = i == manager.selected;
|
|
||||||
auto isRequested = i == manager.pendingSelected;
|
auto isRequested = i == manager.pendingSelected;
|
||||||
|
|
||||||
auto font = isDirty ? font::ITALICS : font::REGULAR;
|
auto font = isDirty ? font::ITALICS : font::REGULAR;
|
||||||
@@ -65,8 +64,6 @@ namespace anm2ed::documents
|
|||||||
else
|
else
|
||||||
manager.close(i);
|
manager.close(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSelected) document.update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document_manager.h"
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "taskbar.h"
|
#include "taskbar.h"
|
||||||
|
|
||||||
@@ -17,6 +17,6 @@ namespace anm2ed::documents
|
|||||||
public:
|
public:
|
||||||
float height{};
|
float height{};
|
||||||
|
|
||||||
void update(taskbar::Taskbar& taskbar, document_manager::DocumentManager& manager, resources::Resources& resources);
|
void update(taskbar::Taskbar&, manager::Manager&, resources::Resources&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,21 @@
|
|||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
|
|
||||||
namespace anm2ed::events
|
namespace anm2ed::events
|
||||||
{
|
{
|
||||||
void Events::update(DocumentManager& manager, Settings& settings, Resources& resources)
|
void Events::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
auto& document = *manager.get();
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& selection = document.selectedEvents;
|
auto& unused = document.unusedEventIDs;
|
||||||
|
auto& hovered = document.hoveredEvent;
|
||||||
if (document.is_just_changed(change::EVENTS)) unusedEventIDs = anm2.events_unused();
|
auto& multiSelect = document.eventMultiSelect;
|
||||||
|
|
||||||
storage.user_data_set(&selection);
|
|
||||||
|
|
||||||
if (ImGui::Begin("Events", &settings.windowIsEvents))
|
if (ImGui::Begin("Events", &settings.windowIsEvents))
|
||||||
{
|
{
|
||||||
@@ -26,17 +25,16 @@ namespace anm2ed::events
|
|||||||
|
|
||||||
if (ImGui::BeginChild("##Events Child", childSize, true))
|
if (ImGui::BeginChild("##Events Child", childSize, true))
|
||||||
{
|
{
|
||||||
storage.begin(anm2.content.events.size());
|
multiSelect.start(anm2.content.events.size());
|
||||||
|
|
||||||
for (auto& [id, event] : anm2.content.events)
|
for (auto& [id, event] : anm2.content.events)
|
||||||
{
|
{
|
||||||
auto isSelected = selection.contains(id);
|
|
||||||
|
|
||||||
ImGui::PushID(id);
|
ImGui::PushID(id);
|
||||||
ImGui::SetNextItemSelectionUserData(id);
|
ImGui::SetNextItemSelectionUserData(id);
|
||||||
if (imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
|
if (imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
|
||||||
event.name, isSelected, 0, &isRenamed))
|
event.name, multiSelect.contains(id), 0, &isRenamed))
|
||||||
if (isRenamed) document.change(change::EVENTS);
|
if (ImGui::IsItemHovered()) hovered = id;
|
||||||
|
if (isRenamed) document.change(change::EVENTS);
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
{
|
{
|
||||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||||
@@ -47,34 +45,67 @@ namespace anm2ed::events
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.end();
|
multiSelect.finish();
|
||||||
|
|
||||||
|
auto copy = [&]()
|
||||||
|
{
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
std::string clipboardText{};
|
||||||
|
for (auto& id : multiSelect)
|
||||||
|
clipboardText += anm2.content.events[id].to_string(id);
|
||||||
|
clipboard.set(clipboardText);
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
clipboard.set(anm2.content.events[hovered].to_string(hovered));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto paste = [&](merge::Type type)
|
||||||
|
{
|
||||||
|
auto clipboardText = clipboard.get();
|
||||||
|
document.events_deserialize(clipboardText, type);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
|
||||||
|
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste(merge::APPEND);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
|
||||||
|
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(clipboard.is_empty());
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Paste"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||||
|
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
if (ImGui::Button("Add", widgetSize))
|
if (ImGui::Button("Add", widgetSize)) document.event_add();
|
||||||
{
|
|
||||||
int id{};
|
|
||||||
anm2.event_add(id);
|
|
||||||
selection = {id};
|
|
||||||
document.change(change::EVENTS);
|
|
||||||
}
|
|
||||||
imgui::set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
|
imgui::set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
ImGui::BeginDisabled(unusedEventIDs.empty());
|
ImGui::BeginDisabled(unused.empty());
|
||||||
{
|
if (ImGui::Button("Remove Unused", widgetSize)) document.events_remove_unused();
|
||||||
if (ImGui::Button("Remove Unused", widgetSize))
|
|
||||||
{
|
|
||||||
for (auto& id : unusedEventIDs)
|
|
||||||
anm2.content.events.erase(id);
|
|
||||||
document.change(change::EVENTS);
|
|
||||||
unusedEventIDs.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
imgui::set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)",
|
imgui::set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)",
|
||||||
settings.shortcutRemove);
|
settings.shortcutRemove);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document_manager.h"
|
#include "clipboard.h"
|
||||||
#include "imgui.h"
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -9,11 +9,8 @@ namespace anm2ed::events
|
|||||||
{
|
{
|
||||||
class Events
|
class Events
|
||||||
{
|
{
|
||||||
imgui::MultiSelectStorage storage{};
|
|
||||||
std::set<int> unusedEventIDs{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&, clipboard::Clipboard&);
|
||||||
resources::Resources& resources);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,15 @@
|
|||||||
namespace anm2ed::filesystem
|
namespace anm2ed::filesystem
|
||||||
{
|
{
|
||||||
std::string path_preferences_get();
|
std::string path_preferences_get();
|
||||||
bool path_is_exist(const std::string& path);
|
bool path_is_exist(const std::string&);
|
||||||
bool path_is_extension(const std::string& path, const std::string& extension);
|
bool path_is_extension(const std::string&, const std::string&);
|
||||||
|
|
||||||
class WorkingDirectory
|
class WorkingDirectory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::filesystem::path previous;
|
std::filesystem::path previous;
|
||||||
|
|
||||||
WorkingDirectory(const std::string& path, bool isFile = false);
|
WorkingDirectory(const std::string&, bool = false);
|
||||||
~WorkingDirectory();
|
~WorkingDirectory();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5230,9 +5230,9 @@ namespace anm2ed::font
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Font();
|
Font();
|
||||||
Font(void* data, size_t length, int size);
|
Font(void*, size_t, int);
|
||||||
~Font();
|
~Font();
|
||||||
ImFont* get();
|
ImFont* get();
|
||||||
Font& operator=(Font&& other) noexcept;
|
Font& operator=(Font&&) noexcept;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::math;
|
using namespace anm2ed::math;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
using namespace glm;
|
using namespace glm;
|
||||||
@@ -17,7 +17,7 @@ using namespace glm;
|
|||||||
namespace anm2ed::frame_properties
|
namespace anm2ed::frame_properties
|
||||||
{
|
{
|
||||||
|
|
||||||
void FrameProperties::update(DocumentManager& manager, Settings& settings)
|
void FrameProperties::update(Manager& manager, Settings& settings)
|
||||||
{
|
{
|
||||||
if (ImGui::Begin("Frame Properties", &settings.windowIsFrameProperties))
|
if (ImGui::Begin("Frame Properties", &settings.windowIsFrameProperties))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document_manager.h"
|
#include "manager.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
namespace anm2ed::frame_properties
|
namespace anm2ed::frame_properties
|
||||||
@@ -8,6 +8,6 @@ namespace anm2ed::frame_properties
|
|||||||
class FrameProperties
|
class FrameProperties
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void update(document_manager::DocumentManager& manager, settings::Settings& settings);
|
void update(manager::Manager&, settings::Settings&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ namespace icon
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
constexpr auto TARGET_DATA = R"(
|
constexpr auto TARGET_DATA = R"(
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="#FFF" stroke-width="1" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="12" r="3.5"/> <line x1="12" y1="-2" x2="12" y2="26"/> <line x1="-2" y1="12" x2="26" y2="12"/> </svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="#FFF" stroke-width="0.75" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="12" r="2.8"/> <line x1="12" y1="5" x2="12" y2="19"/> <line x1="5" y1="12" x2="19" y2="12"/> </svg>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
constexpr auto INTERPOLATED_DATA = R"(
|
constexpr auto INTERPOLATED_DATA = R"(
|
||||||
@@ -142,7 +142,7 @@ namespace icon
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
#define LIST \
|
#define LIST \
|
||||||
X(NONE, NONE_DATA, SIZE_NORMAL) \
|
X(NONE, NONE_DATA, SIZE_SMALL) \
|
||||||
X(FILE, FILE_DATA, SIZE_NORMAL) \
|
X(FILE, FILE_DATA, SIZE_NORMAL) \
|
||||||
X(FOLDER, FOLDER_DATA, SIZE_NORMAL) \
|
X(FOLDER, FOLDER_DATA, SIZE_NORMAL) \
|
||||||
X(CLOSE, CLOSE_DATA, SIZE_NORMAL) \
|
X(CLOSE, CLOSE_DATA, SIZE_NORMAL) \
|
||||||
|
|||||||
@@ -252,19 +252,15 @@ namespace anm2ed::imgui
|
|||||||
internal.AdapterSetItemSelected = external_storage_set;
|
internal.AdapterSetItemSelected = external_storage_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSelectStorage::user_data_set(std::set<int>* userData)
|
void MultiSelectStorage::start(size_t size)
|
||||||
{
|
{
|
||||||
internal.UserData = userData;
|
internal.UserData = this;
|
||||||
this->userData = userData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MultiSelectStorage::begin(size_t size)
|
auto io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, this->size(), size);
|
||||||
{
|
|
||||||
auto io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, userData ? userData->size() : 0, size);
|
|
||||||
internal.ApplyRequests(io);
|
internal.ApplyRequests(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSelectStorage::end()
|
void MultiSelectStorage::finish()
|
||||||
{
|
{
|
||||||
auto io = ImGui::EndMultiSelect();
|
auto io = ImGui::EndMultiSelect();
|
||||||
internal.ApplyRequests(io);
|
internal.ApplyRequests(io);
|
||||||
@@ -281,6 +277,7 @@ namespace anm2ed::imgui
|
|||||||
{
|
{
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
isTriggered = true;
|
isTriggered = true;
|
||||||
|
isJustOpened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PopupHelper::trigger()
|
void PopupHelper::trigger()
|
||||||
@@ -296,6 +293,11 @@ namespace anm2ed::imgui
|
|||||||
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * percent));
|
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * percent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PopupHelper::end()
|
||||||
|
{
|
||||||
|
isJustOpened = false;
|
||||||
|
}
|
||||||
|
|
||||||
void PopupHelper::close()
|
void PopupHelper::close()
|
||||||
{
|
{
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
|
|||||||
58
src/imgui.h
58
src/imgui.h
@@ -124,37 +124,43 @@ namespace anm2ed::imgui
|
|||||||
{"Super", ImGuiMod_Super},
|
{"Super", ImGuiMod_Super},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string chord_to_string(ImGuiKeyChord chord);
|
std::string chord_to_string(ImGuiKeyChord);
|
||||||
ImGuiKeyChord string_to_chord(const std::string& string);
|
ImGuiKeyChord string_to_chord(const std::string&);
|
||||||
float row_widget_width_get(int count, float width = ImGui::GetContentRegionAvail().x);
|
float row_widget_width_get(int, float = ImGui::GetContentRegionAvail().x);
|
||||||
ImVec2 widget_size_with_row_get(int count, float width = ImGui::GetContentRegionAvail().x);
|
ImVec2 widget_size_with_row_get(int, float = ImGui::GetContentRegionAvail().x);
|
||||||
float footer_height_get(int itemCount = 1);
|
float footer_height_get(int = 1);
|
||||||
ImVec2 footer_size_get(int itemCount = 1);
|
ImVec2 footer_size_get(int = 1);
|
||||||
ImVec2 size_without_footer_get(int rowCount = 1);
|
ImVec2 size_without_footer_get(int = 1);
|
||||||
ImVec2 child_size_get(int rowCount = 1);
|
ImVec2 child_size_get(int = 1);
|
||||||
int input_text_callback(ImGuiInputTextCallbackData* data);
|
int input_text_callback(ImGuiInputTextCallbackData*);
|
||||||
bool input_text_string(const char* label, std::string* string, ImGuiInputTextFlags flags = 0);
|
bool input_text_string(const char*, std::string*, ImGuiInputTextFlags = 0);
|
||||||
void combo_strings(const std::string& label, int* index, std::vector<std::string>& strings);
|
void combo_strings(const std::string&, int*, std::vector<std::string>&);
|
||||||
void combo_strings(const std::string& label, int* index, std::vector<const char*>& strings);
|
void combo_strings(const std::string&, int*, std::vector<const char*>&);
|
||||||
bool selectable_input_text(const std::string& label, const std::string& id, std::string& text,
|
bool selectable_input_text(const std::string&, const std::string&, std::string&, bool = false,
|
||||||
bool isSelected = false, ImGuiSelectableFlags flags = 0, bool* isRenamed = nullptr);
|
ImGuiSelectableFlags = 0, bool* = nullptr);
|
||||||
void set_item_tooltip_shortcut(const char* tooltip, const std::string& shortcut = {});
|
void set_item_tooltip_shortcut(const char*, const std::string& = {});
|
||||||
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected);
|
void external_storage_set(ImGuiSelectionExternalStorage*, int, bool);
|
||||||
ImVec2 icon_size_get();
|
ImVec2 icon_size_get();
|
||||||
bool chord_held(ImGuiKeyChord chord);
|
bool chord_held(ImGuiKeyChord);
|
||||||
bool chord_repeating(ImGuiKeyChord chord, float delay = 0.125f, float rate = 0.025f);
|
bool chord_repeating(ImGuiKeyChord, float = 0.125f, float = 0.025f);
|
||||||
bool shortcut(std::string string, types::shortcut::Type type = types::shortcut::FOCUSED_SET);
|
bool shortcut(std::string, types::shortcut::Type = types::shortcut::FOCUSED_SET);
|
||||||
|
|
||||||
class MultiSelectStorage
|
class MultiSelectStorage : public std::set<int>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ImGuiSelectionExternalStorage internal{};
|
ImGuiSelectionExternalStorage internal{};
|
||||||
std::set<int>* userData{};
|
using std::set<int>::set;
|
||||||
|
using std::set<int>::operator=;
|
||||||
|
using std::set<int>::begin;
|
||||||
|
using std::set<int>::rbegin;
|
||||||
|
using std::set<int>::end;
|
||||||
|
using std::set<int>::size;
|
||||||
|
using std::set<int>::insert;
|
||||||
|
using std::set<int>::erase;
|
||||||
|
|
||||||
MultiSelectStorage();
|
MultiSelectStorage();
|
||||||
void user_data_set(std::set<int>* userData);
|
void start(size_t);
|
||||||
void begin(size_t size);
|
void finish();
|
||||||
void end();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PopupHelper
|
class PopupHelper
|
||||||
@@ -163,12 +169,14 @@ namespace anm2ed::imgui
|
|||||||
const char* label{};
|
const char* label{};
|
||||||
bool isOpen{};
|
bool isOpen{};
|
||||||
bool isTriggered{};
|
bool isTriggered{};
|
||||||
|
bool isJustOpened{};
|
||||||
bool isNoHeight{};
|
bool isNoHeight{};
|
||||||
float percent{};
|
float percent{};
|
||||||
|
|
||||||
PopupHelper(const char* label, float percent = POPUP_NORMAL, bool isNoHeight = false);
|
PopupHelper(const char*, float = POPUP_NORMAL, bool = false);
|
||||||
void open();
|
void open();
|
||||||
void trigger();
|
void trigger();
|
||||||
|
void end();
|
||||||
void close();
|
void close();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
146
src/layers.cpp
146
src/layers.cpp
@@ -2,38 +2,24 @@
|
|||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
using namespace anm2ed::document;
|
using namespace anm2ed::document;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
using namespace anm2ed::util;
|
|
||||||
|
|
||||||
namespace anm2ed::layers
|
namespace anm2ed::layers
|
||||||
{
|
{
|
||||||
void Layers::update(Document& document, Settings& settings, Resources& resources)
|
void Layers::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& selection = document.selectedLayers;
|
auto& reference = document.referenceLayer;
|
||||||
auto& referenceLayer = document.referenceLayer;
|
auto& unused = document.unusedLayerIDs;
|
||||||
|
auto& hovered = document.hoveredLayer;
|
||||||
if (document.is_just_changed(change::LAYERS)) unusedLayerIDs = anm2.layers_unused();
|
auto& multiSelect = document.layersMultiSelect;
|
||||||
|
auto& propertiesPopup = manager.layerPropertiesPopup;
|
||||||
storage.user_data_set(&selection);
|
|
||||||
|
|
||||||
auto properties_popup_open = [&](int id = -1)
|
|
||||||
{
|
|
||||||
if (id == -1)
|
|
||||||
{
|
|
||||||
isAdd = true;
|
|
||||||
editLayer = anm2::Layer();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
editLayer = anm2.content.layers.at(id);
|
|
||||||
|
|
||||||
propertiesPopup.open();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ImGui::Begin("Layers", &settings.windowIsLayers))
|
if (ImGui::Begin("Layers", &settings.windowIsLayers))
|
||||||
{
|
{
|
||||||
@@ -41,26 +27,24 @@ namespace anm2ed::layers
|
|||||||
|
|
||||||
if (ImGui::BeginChild("##Layers Child", childSize, true))
|
if (ImGui::BeginChild("##Layers Child", childSize, true))
|
||||||
{
|
{
|
||||||
storage.begin(anm2.content.layers.size());
|
multiSelect.start(anm2.content.layers.size());
|
||||||
|
|
||||||
for (auto& [id, layer] : anm2.content.layers)
|
for (auto& [id, layer] : anm2.content.layers)
|
||||||
{
|
{
|
||||||
auto isSelected = selection.contains(id);
|
auto isSelected = multiSelect.contains(id);
|
||||||
auto isReferenced = referenceLayer == id;
|
|
||||||
|
|
||||||
ImGui::PushID(id);
|
ImGui::PushID(id);
|
||||||
|
|
||||||
ImGui::SetNextItemSelectionUserData(id);
|
ImGui::SetNextItemSelectionUserData(id);
|
||||||
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
ImGui::Selectable(std::format(anm2::LAYER_FORMAT, id, layer.name, layer.spritesheetID).c_str(), isSelected);
|
||||||
ImGui::Selectable(std::format("#{} {} (Spritesheet: #{})", id, layer.name, layer.spritesheetID).c_str(),
|
if (ImGui::IsItemHovered())
|
||||||
isSelected);
|
|
||||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
|
||||||
{
|
{
|
||||||
referenceLayer = id;
|
hovered = id;
|
||||||
properties_popup_open(id);
|
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) manager.layer_properties_open(id);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
hovered = -1;
|
||||||
|
|
||||||
if (isReferenced) ImGui::PopFont();
|
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
{
|
{
|
||||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||||
@@ -73,50 +57,83 @@ namespace anm2ed::layers
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.end();
|
multiSelect.finish();
|
||||||
|
|
||||||
|
auto copy = [&]()
|
||||||
|
{
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
std::string clipboardText{};
|
||||||
|
for (auto& id : multiSelect)
|
||||||
|
clipboardText += anm2.content.layers[id].to_string(id);
|
||||||
|
clipboard.set(clipboardText);
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
clipboard.set(anm2.content.layers[hovered].to_string(hovered));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto paste = [&](merge::Type type)
|
||||||
|
{
|
||||||
|
auto clipboardText = clipboard.get();
|
||||||
|
document.layers_deserialize(clipboardText, type);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
|
||||||
|
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste(merge::APPEND);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
|
||||||
|
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(clipboard.is_empty());
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Paste"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||||
|
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
if (ImGui::Button("Add", widgetSize)) properties_popup_open();
|
if (ImGui::Button("Add", widgetSize)) manager.layer_properties_open();
|
||||||
imgui::set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd);
|
imgui::set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
ImGui::BeginDisabled(unusedLayerIDs.empty());
|
ImGui::BeginDisabled(unused.empty());
|
||||||
{
|
if (ImGui::Button("Remove Unused", widgetSize)) document.layers_remove_unused();
|
||||||
if (ImGui::Button("Remove Unused", widgetSize))
|
|
||||||
{
|
|
||||||
for (auto& id : unusedLayerIDs)
|
|
||||||
anm2.content.layers.erase(id);
|
|
||||||
document.change(change::LAYERS);
|
|
||||||
unusedLayerIDs.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
imgui::set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
|
imgui::set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
|
||||||
settings.shortcutRemove);
|
settings.shortcutRemove);
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
propertiesPopup.trigger();
|
manager.layer_properties_trigger();
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
|
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||||
{
|
{
|
||||||
auto childSize = imgui::child_size_get(2);
|
auto childSize = imgui::child_size_get(2);
|
||||||
auto& layer = editLayer;
|
auto& layer = manager.editLayer;
|
||||||
|
|
||||||
auto close = [&]()
|
|
||||||
{
|
|
||||||
isAdd = false;
|
|
||||||
editLayer = anm2::Layer();
|
|
||||||
propertiesPopup.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
|
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
|
||||||
imgui::input_text_string("Name", &layer.name);
|
imgui::input_text_string("Name", &layer.name);
|
||||||
ImGui::SetItemTooltip("Set the item's name.");
|
ImGui::SetItemTooltip("Set the item's name.");
|
||||||
imgui::combo_strings("Spritesheet", &layer.spritesheetID, document.spritesheetNames);
|
imgui::combo_strings("Spritesheet", &layer.spritesheetID, document.spritesheetNames);
|
||||||
@@ -126,27 +143,18 @@ namespace anm2ed::layers
|
|||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
if (ImGui::Button(isAdd ? "Add" : "Confirm", widgetSize))
|
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
|
||||||
{
|
{
|
||||||
if (isAdd)
|
document.layer_set(layer);
|
||||||
{
|
manager.layer_properties_close();
|
||||||
auto id = map::next_id_get(anm2.content.layers);
|
|
||||||
anm2.content.layers[id] = editLayer;
|
|
||||||
referenceLayer = id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
anm2.content.layers[referenceLayer] = editLayer;
|
|
||||||
document.change(change::LAYERS);
|
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (ImGui::Button("Cancel", widgetSize)) close();
|
if (ImGui::Button("Cancel", widgetSize)) manager.layer_properties_close();
|
||||||
|
|
||||||
|
manager.layer_properties_end();
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
referenceLayer = propertiesPopup.isOpen ? referenceLayer : -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/layers.h
13
src/layers.h
@@ -1,22 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document.h"
|
#include "clipboard.h"
|
||||||
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#include "imgui.h"
|
|
||||||
|
|
||||||
namespace anm2ed::layers
|
namespace anm2ed::layers
|
||||||
{
|
{
|
||||||
class Layers
|
class Layers
|
||||||
{
|
{
|
||||||
bool isAdd{};
|
|
||||||
imgui::PopupHelper propertiesPopup{imgui::PopupHelper("Layer Properties", imgui::POPUP_SMALL, true)};
|
|
||||||
imgui::MultiSelectStorage storage;
|
|
||||||
anm2::Layer editLayer{};
|
|
||||||
std::set<int> unusedLayerIDs{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document::Document& document, settings::Settings& settings, resources::Resources& resources);
|
void update(manager::Manager&, settings::Settings&, resources::Resources&, clipboard::Clipboard&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ namespace anm2ed::loader
|
|||||||
std::vector<std::string> arguments;
|
std::vector<std::string> arguments;
|
||||||
bool isError{};
|
bool isError{};
|
||||||
|
|
||||||
Loader(int argc, const char** argv);
|
Loader(int, const char**);
|
||||||
~Loader();
|
~Loader();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/log.h
12
src/log.h
@@ -30,12 +30,12 @@ namespace anm2ed::log
|
|||||||
std::ofstream file{};
|
std::ofstream file{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void write(const Level level, const std::string& message);
|
void write(const Level, const std::string&);
|
||||||
void info(const std::string& message);
|
void info(const std::string&);
|
||||||
void warning(const std::string& message);
|
void warning(const std::string&);
|
||||||
void error(const std::string& message);
|
void error(const std::string&);
|
||||||
void fatal(const std::string& message);
|
void fatal(const std::string&);
|
||||||
void open(const std::filesystem::path& path);
|
void open(const std::filesystem::path&);
|
||||||
Logger();
|
Logger();
|
||||||
~Logger();
|
~Logger();
|
||||||
};
|
};
|
||||||
|
|||||||
120
src/manager.cpp
Normal file
120
src/manager.cpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#include "toast.h"
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using namespace anm2ed::toast;
|
||||||
|
using namespace anm2ed::types;
|
||||||
|
using namespace anm2ed::util;
|
||||||
|
|
||||||
|
namespace anm2ed::manager
|
||||||
|
{
|
||||||
|
Document* Manager::get(int index)
|
||||||
|
{
|
||||||
|
return vector::find(documents, index > -1 ? index : selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::open(const std::string& path, bool isNew)
|
||||||
|
{
|
||||||
|
std::string errorString{};
|
||||||
|
Document document = Document(path, isNew, &errorString);
|
||||||
|
if (document.is_valid())
|
||||||
|
{
|
||||||
|
documents.emplace_back(std::move(document));
|
||||||
|
selected = documents.size() - 1;
|
||||||
|
pendingSelected = selected;
|
||||||
|
toasts.info(std::format("Initialized document: {}", path));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
toasts.error(std::format("Failed to initialize document: {} ({})", path, errorString));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::new_(const std::string& path)
|
||||||
|
{
|
||||||
|
open(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::save(int index, const std::string& path)
|
||||||
|
{
|
||||||
|
if (auto document = get(index); document)
|
||||||
|
{
|
||||||
|
std::string errorString{};
|
||||||
|
document->path = !path.empty() ? path : document->path.string();
|
||||||
|
document->save(document->path, &errorString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::save(const std::string& path)
|
||||||
|
{
|
||||||
|
save(selected, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::close(int index)
|
||||||
|
{
|
||||||
|
documents.erase(documents.begin() + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::layer_properties_open(int id)
|
||||||
|
{
|
||||||
|
if (auto document = get(); document)
|
||||||
|
{
|
||||||
|
if (id == -1)
|
||||||
|
editLayer = anm2::Layer();
|
||||||
|
else
|
||||||
|
editLayer = document->anm2.content.layers.at(id);
|
||||||
|
|
||||||
|
document->referenceLayer = id;
|
||||||
|
|
||||||
|
layerPropertiesPopup.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::layer_properties_trigger()
|
||||||
|
{
|
||||||
|
layerPropertiesPopup.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::layer_properties_end()
|
||||||
|
{
|
||||||
|
layerPropertiesPopup.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::layer_properties_close()
|
||||||
|
{
|
||||||
|
editLayer = anm2::Layer();
|
||||||
|
layerPropertiesPopup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::null_properties_open(int id)
|
||||||
|
{
|
||||||
|
if (auto document = get(); document)
|
||||||
|
{
|
||||||
|
if (id == -1)
|
||||||
|
editNull = anm2::Null();
|
||||||
|
else
|
||||||
|
editNull = document->anm2.content.nulls.at(id);
|
||||||
|
|
||||||
|
document->referenceNull = id;
|
||||||
|
|
||||||
|
nullPropertiesPopup.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::null_properties_trigger()
|
||||||
|
{
|
||||||
|
nullPropertiesPopup.trigger();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::null_properties_end()
|
||||||
|
{
|
||||||
|
nullPropertiesPopup.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Manager::null_properties_close()
|
||||||
|
{
|
||||||
|
editNull = anm2::Null();
|
||||||
|
nullPropertiesPopup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
src/manager.h
Normal file
40
src/manager.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "document.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
using namespace anm2ed::document;
|
||||||
|
|
||||||
|
namespace anm2ed::manager
|
||||||
|
{
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<Document> documents{};
|
||||||
|
int selected{};
|
||||||
|
int pendingSelected{};
|
||||||
|
|
||||||
|
anm2::Layer editLayer{};
|
||||||
|
imgui::PopupHelper layerPropertiesPopup{imgui::PopupHelper("Layer Properties", imgui::POPUP_SMALL, true)};
|
||||||
|
|
||||||
|
anm2::Null editNull{};
|
||||||
|
imgui::PopupHelper nullPropertiesPopup{imgui::PopupHelper("Null Properties", imgui::POPUP_SMALL, true)};
|
||||||
|
|
||||||
|
Document* get(int = -1);
|
||||||
|
void open(const std::string&, bool = false);
|
||||||
|
void new_(const std::string&);
|
||||||
|
void save(int, const std::string& = {});
|
||||||
|
void save(const std::string& = {});
|
||||||
|
void close(int);
|
||||||
|
void layer_properties_open(int = -1);
|
||||||
|
void layer_properties_trigger();
|
||||||
|
void layer_properties_end();
|
||||||
|
void layer_properties_close();
|
||||||
|
void null_properties_open(int = -1);
|
||||||
|
void null_properties_trigger();
|
||||||
|
void null_properties_end();
|
||||||
|
void null_properties_close();
|
||||||
|
};
|
||||||
|
}
|
||||||
15
src/math.h
15
src/math.h
@@ -31,16 +31,15 @@ namespace anm2ed::math
|
|||||||
1.0f, 1.0f, uvMax.x, uvMax.y, 0.0f, 1.0f, uvMin.x, uvMax.y};
|
1.0f, 1.0f, uvMax.x, uvMax.y, 0.0f, 1.0f, uvMin.x, uvMax.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
float round_nearest_multiple(float value, float multiple);
|
float round_nearest_multiple(float, float);
|
||||||
|
|
||||||
int float_decimals_needed(float value);
|
int float_decimals_needed(float);
|
||||||
|
|
||||||
const char* float_format_get(float value);
|
const char* float_format_get(float);
|
||||||
|
|
||||||
const char* vec2_format_get(glm::vec2& value);
|
const char* vec2_format_get(glm::vec2&);
|
||||||
|
|
||||||
glm::mat4 quad_model_get(glm::vec2 size = {}, glm::vec2 position = {}, glm::vec2 pivot = {},
|
glm::mat4 quad_model_get(glm::vec2 = {}, glm::vec2 = {}, glm::vec2 = {},
|
||||||
glm::vec2 scale = glm::vec2(1.0f), float rotation = {});
|
glm::vec2 = glm::vec2(1.0f), float = {});
|
||||||
glm::mat4 quad_model_parent_get(glm::vec2 position = {}, glm::vec2 pivot = {}, glm::vec2 scale = glm::vec2(1.0f),
|
glm::mat4 quad_model_parent_get(glm::vec2 = {}, glm::vec2 = {}, glm::vec2 = glm::vec2(1.0f), float = {});
|
||||||
float rotation = {});
|
|
||||||
}
|
}
|
||||||
129
src/nulls.cpp
129
src/nulls.cpp
@@ -2,21 +2,23 @@
|
|||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace anm2ed::document;
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
|
|
||||||
namespace anm2ed::nulls
|
namespace anm2ed::nulls
|
||||||
{
|
{
|
||||||
void Nulls::update(Document& document, int& documentIndex, Settings& settings, Resources& resources)
|
void Nulls::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& selection = document.selectedNulls;
|
auto& reference = document.referenceNull;
|
||||||
|
auto& unused = document.unusedNullIDs;
|
||||||
if (document.is_just_changed(change::NULLS)) unusedNullsIDs = anm2.nulls_unused();
|
auto& hovered = document.hoveredNull;
|
||||||
|
auto& multiSelect = document.nullMultiSelect;
|
||||||
storage.user_data_set(&selection);
|
auto& propertiesPopup = manager.nullPropertiesPopup;
|
||||||
|
|
||||||
if (ImGui::Begin("Nulls", &settings.windowIsNulls))
|
if (ImGui::Begin("Nulls", &settings.windowIsNulls))
|
||||||
{
|
{
|
||||||
@@ -24,17 +26,25 @@ namespace anm2ed::nulls
|
|||||||
|
|
||||||
if (ImGui::BeginChild("##Nulls Child", childSize, true))
|
if (ImGui::BeginChild("##Nulls Child", childSize, true))
|
||||||
{
|
{
|
||||||
storage.begin(anm2.content.nulls.size());
|
multiSelect.start(anm2.content.nulls.size());
|
||||||
|
|
||||||
for (auto& [id, null] : anm2.content.nulls)
|
for (auto& [id, null] : anm2.content.nulls)
|
||||||
{
|
{
|
||||||
auto isSelected = selection.contains(id);
|
auto isSelected = multiSelect.contains(id);
|
||||||
|
auto isReferenced = reference == id;
|
||||||
|
|
||||||
ImGui::PushID(id);
|
ImGui::PushID(id);
|
||||||
ImGui::SetNextItemSelectionUserData(id);
|
ImGui::SetNextItemSelectionUserData(id);
|
||||||
imgui::selectable_input_text(std::format("#{} {}", id, null.name),
|
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
||||||
std::format("###Document #{} Null #{}", documentIndex, id), null.name,
|
ImGui::Selectable(std::format(anm2::NULL_FORMAT, id, null.name).c_str(), isSelected);
|
||||||
isSelected);
|
if (ImGui::IsItemHovered())
|
||||||
|
{
|
||||||
|
hovered = id;
|
||||||
|
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) manager.null_properties_open(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReferenced) ImGui::PopFont();
|
||||||
|
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
{
|
{
|
||||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||||
@@ -46,29 +56,106 @@ namespace anm2ed::nulls
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.end();
|
multiSelect.finish();
|
||||||
|
|
||||||
|
auto copy = [&]()
|
||||||
|
{
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
std::string clipboardText{};
|
||||||
|
for (auto& id : multiSelect)
|
||||||
|
clipboardText += anm2.content.nulls[id].to_string(id);
|
||||||
|
clipboard.set(clipboardText);
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
clipboard.set(anm2.content.nulls[hovered].to_string(hovered));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto paste = [&](merge::Type type)
|
||||||
|
{
|
||||||
|
auto clipboardText = clipboard.get();
|
||||||
|
document.nulls_deserialize(clipboardText, type);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
|
||||||
|
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste(merge::APPEND);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
|
||||||
|
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(clipboard.is_empty());
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Paste"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||||
|
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
ImGui::Button("Add", widgetSize);
|
if (ImGui::Button("Add", widgetSize)) manager.null_properties_open();
|
||||||
imgui::set_item_tooltip_shortcut("Add a null.", settings.shortcutAdd);
|
imgui::set_item_tooltip_shortcut("Add a null.", settings.shortcutAdd);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
ImGui::BeginDisabled(unusedNullsIDs.empty());
|
ImGui::BeginDisabled(unused.empty());
|
||||||
{
|
if (ImGui::Button("Remove Unused", widgetSize)) document.nulls_remove_unused();
|
||||||
if (ImGui::Button("Remove Unused", widgetSize))
|
|
||||||
for (auto& id : unusedNullsIDs)
|
|
||||||
anm2.content.nulls.erase(id);
|
|
||||||
document.change(change::NULLS);
|
|
||||||
}
|
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
imgui::set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)",
|
imgui::set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)",
|
||||||
settings.shortcutRemove);
|
settings.shortcutRemove);
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
manager.null_properties_trigger();
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||||
|
{
|
||||||
|
auto childSize = imgui::child_size_get(2);
|
||||||
|
auto& null = manager.editNull;
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
|
||||||
|
{
|
||||||
|
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
|
||||||
|
imgui::input_text_string("Name", &null.name);
|
||||||
|
ImGui::SetItemTooltip("Set the null's name.");
|
||||||
|
|
||||||
|
ImGui::Checkbox("Rect", &null.isShowRect);
|
||||||
|
ImGui::SetItemTooltip("The null will have a rectangle show around it.");
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
|
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
|
||||||
|
{
|
||||||
|
document.null_set(null);
|
||||||
|
manager.null_properties_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button("Cancel", widgetSize)) manager.null_properties_close();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.null_properties_end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
src/nulls.h
10
src/nulls.h
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "document.h"
|
#include "clipboard.h"
|
||||||
#include "imgui.h"
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -9,11 +9,7 @@ namespace anm2ed::nulls
|
|||||||
{
|
{
|
||||||
class Nulls
|
class Nulls
|
||||||
{
|
{
|
||||||
imgui::MultiSelectStorage storage{};
|
|
||||||
std::set<int> unusedNullsIDs{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document::Document& document, int& documentIndex, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&, clipboard::Clipboard&);
|
||||||
resources::Resources& resources);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
@@ -24,7 +25,6 @@ namespace anm2ed::onionskin
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
};
|
};
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutOnionskin);
|
|
||||||
ImGui::Checkbox("Enabled", &settings.onionskinIsEnabled);
|
ImGui::Checkbox("Enabled", &settings.onionskinIsEnabled);
|
||||||
|
|
||||||
order_configure("Before", settings.onionskinBeforeCount, settings.onionskinBeforeColor);
|
order_configure("Before", settings.onionskinBeforeCount, settings.onionskinBeforeColor);
|
||||||
@@ -32,14 +32,14 @@ namespace anm2ed::onionskin
|
|||||||
|
|
||||||
ImGui::Text("Order");
|
ImGui::Text("Order");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::RadioButton("Before", &settings.onionskinDrawOrder, BELOW);
|
ImGui::RadioButton("Before", &settings.onionskinDrawOrder, draw_order::BELOW);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::RadioButton("After", &settings.onionskinDrawOrder, ABOVE);
|
ImGui::RadioButton("After", &settings.onionskinDrawOrder, draw_order::ABOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imgui::shortcut(settings.shortcutOnionskin), shortcut::GLOBAL)
|
|
||||||
settings.onionskinIsEnabled = !settings.onionskinIsEnabled;
|
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutOnionskin, shortcut::GLOBAL))
|
||||||
|
settings.onionskinIsEnabled = !settings.onionskinIsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,9 @@
|
|||||||
|
|
||||||
namespace anm2ed::onionskin
|
namespace anm2ed::onionskin
|
||||||
{
|
{
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
BELOW,
|
|
||||||
ABOVE
|
|
||||||
};
|
|
||||||
|
|
||||||
class Onionskin
|
class Onionskin
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void update(settings::Settings& settings);
|
void update(settings::Settings&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ namespace anm2ed::playback
|
|||||||
bool isFinished{};
|
bool isFinished{};
|
||||||
|
|
||||||
void toggle();
|
void toggle();
|
||||||
void clamp(int length);
|
void clamp(int);
|
||||||
void tick(int fps, int length, bool isLoop);
|
void tick(int, int, bool);
|
||||||
void decrement(int length);
|
void decrement(int);
|
||||||
void increment(int length);
|
void increment(int);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -206,8 +206,8 @@ namespace anm2ed::settings
|
|||||||
|
|
||||||
Settings();
|
Settings();
|
||||||
|
|
||||||
Settings(const std::string& path);
|
Settings(const std::string&);
|
||||||
void save(const std::string& path, const std::string& imguiData);
|
void save(const std::string&, const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ShortcutType
|
enum ShortcutType
|
||||||
|
|||||||
31
src/shader.h
31
src/shader.h
@@ -39,19 +39,6 @@ namespace anm2ed::shader
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
constexpr auto GRID_VERTEX = R"(
|
|
||||||
#version 330 core
|
|
||||||
layout (location = 0) in vec2 i_position;
|
|
||||||
layout (location = 1) in vec2 i_uv;
|
|
||||||
|
|
||||||
out vec2 i_uv_out;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
i_uv_out = i_position;
|
|
||||||
gl_Position = vec4(i_position, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
constexpr auto FRAGMENT = R"(
|
constexpr auto FRAGMENT = R"(
|
||||||
#version 330 core
|
#version 330 core
|
||||||
out vec4 o_fragColor;
|
out vec4 o_fragColor;
|
||||||
@@ -78,6 +65,19 @@ void main() {
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
constexpr auto GRID_VERTEX = R"(
|
||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec2 i_position;
|
||||||
|
layout (location = 1) in vec2 i_uv;
|
||||||
|
|
||||||
|
out vec2 i_uv_out;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
i_uv_out = i_position;
|
||||||
|
gl_Position = vec4(i_position, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
constexpr auto GRID_FRAGMENT = R"(
|
constexpr auto GRID_FRAGMENT = R"(
|
||||||
#version 330 core
|
#version 330 core
|
||||||
in vec2 i_uv_out;
|
in vec2 i_uv_out;
|
||||||
@@ -98,6 +98,7 @@ void main() {
|
|||||||
vec2 pan = u_pan;
|
vec2 pan = u_pan;
|
||||||
|
|
||||||
vec2 world = (i_uv_out - (2.0 * pan / viewSize)) * (viewSize / (2.0 * zoom));
|
vec2 world = (i_uv_out - (2.0 * pan / viewSize)) * (viewSize / (2.0 * zoom));
|
||||||
|
world += vec2(0.5); // Half pixel nudge
|
||||||
|
|
||||||
vec2 cell = max(u_size, vec2(1.0));
|
vec2 cell = max(u_size, vec2(1.0));
|
||||||
vec2 grid = (world - u_offset) / cell;
|
vec2 grid = (world - u_offset) / cell;
|
||||||
@@ -148,8 +149,8 @@ void main() {
|
|||||||
GLuint id{};
|
GLuint id{};
|
||||||
|
|
||||||
Shader();
|
Shader();
|
||||||
Shader(const char* vertex, const char* fragment);
|
Shader(const char*, const char*);
|
||||||
Shader& operator=(Shader&& other) noexcept;
|
Shader& operator=(Shader&&) noexcept;
|
||||||
~Shader();
|
~Shader();
|
||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
};
|
};
|
||||||
|
|||||||
68
src/snapshots.cpp
Normal file
68
src/snapshots.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include "snapshots.h"
|
||||||
|
|
||||||
|
namespace anm2ed::snapshots
|
||||||
|
{
|
||||||
|
bool SnapshotStack::is_empty()
|
||||||
|
{
|
||||||
|
return top == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotStack::push(Snapshot& snapshot)
|
||||||
|
{
|
||||||
|
if (top >= MAX)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX - 1; i++)
|
||||||
|
snapshots[i] = snapshots[i + 1];
|
||||||
|
top = MAX - 1;
|
||||||
|
}
|
||||||
|
snapshots[top++] = snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
Snapshot* SnapshotStack::pop()
|
||||||
|
{
|
||||||
|
if (is_empty()) return nullptr;
|
||||||
|
return &snapshots[--top];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotStack::clear()
|
||||||
|
{
|
||||||
|
top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snapshots::push(const anm2::Anm2& anm2, anm2::Reference reference, const std::string& message)
|
||||||
|
{
|
||||||
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
|
undoStack.push(snapshot);
|
||||||
|
redoStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snapshots::undo(anm2::Anm2& anm2, anm2::Reference& reference, std::string& message)
|
||||||
|
{
|
||||||
|
if (auto current = undoStack.pop())
|
||||||
|
{
|
||||||
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
|
redoStack.push(snapshot);
|
||||||
|
anm2 = current->anm2;
|
||||||
|
reference = current->reference;
|
||||||
|
message = current->message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snapshots::redo(anm2::Anm2& anm2, anm2::Reference& reference, std::string& message)
|
||||||
|
{
|
||||||
|
if (auto current = redoStack.pop())
|
||||||
|
{
|
||||||
|
Snapshot snapshot = {anm2, reference, message};
|
||||||
|
undoStack.push(snapshot);
|
||||||
|
anm2 = current->anm2;
|
||||||
|
reference = current->reference;
|
||||||
|
message = current->message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snapshots::reset()
|
||||||
|
{
|
||||||
|
undoStack.clear();
|
||||||
|
redoStack.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
42
src/snapshots.h
Normal file
42
src/snapshots.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "anm2.h"
|
||||||
|
|
||||||
|
namespace anm2ed::snapshots
|
||||||
|
{
|
||||||
|
constexpr auto ACTION = "Action";
|
||||||
|
constexpr auto MAX = 100;
|
||||||
|
|
||||||
|
class Snapshot
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
anm2::Anm2 anm2{};
|
||||||
|
anm2::Reference reference{};
|
||||||
|
std::string message = ACTION;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SnapshotStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Snapshot snapshots[MAX];
|
||||||
|
int top{};
|
||||||
|
|
||||||
|
bool is_empty();
|
||||||
|
void push(Snapshot& snapshot);
|
||||||
|
Snapshot* pop();
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
class Snapshots
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SnapshotStack undoStack{};
|
||||||
|
SnapshotStack redoStack{};
|
||||||
|
|
||||||
|
Snapshot* get();
|
||||||
|
void push(const anm2::Anm2&, anm2::Reference, const std::string&);
|
||||||
|
void undo(anm2::Anm2& anm2, anm2::Reference& reference, std::string&);
|
||||||
|
void redo(anm2::Anm2& anm2, anm2::Reference& reference, std::string&);
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::canvas;
|
using namespace anm2ed::canvas;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
@@ -18,7 +18,7 @@ namespace anm2ed::spritesheet_editor
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpritesheetEditor::update(DocumentManager& manager, Settings& settings, Resources& resources)
|
void SpritesheetEditor::update(Manager& manager, Settings& settings, Resources& resources)
|
||||||
{
|
{
|
||||||
auto& document = *manager.get();
|
auto& document = *manager.get();
|
||||||
auto& pan = document.editorPan;
|
auto& pan = document.editorPan;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "document_manager.h"
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -13,7 +13,6 @@ namespace anm2ed::spritesheet_editor
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SpritesheetEditor();
|
SpritesheetEditor();
|
||||||
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&);
|
||||||
resources::Resources& resources);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
#include "spritesheets.h"
|
#include "spritesheets.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "toast.h"
|
#include "toast.h"
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
using namespace anm2ed::anm2;
|
using namespace anm2ed::anm2;
|
||||||
|
using namespace anm2ed::clipboard;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::dialog;
|
using namespace anm2ed::dialog;
|
||||||
@@ -15,17 +18,72 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace anm2ed::spritesheets
|
namespace anm2ed::spritesheets
|
||||||
{
|
{
|
||||||
void Spritesheets::update(Document& document, Settings& settings, Resources& resources, Dialog& dialog)
|
void Spritesheets::update(Manager& manager, Settings& settings, Resources& resources, Dialog& dialog,
|
||||||
|
Clipboard& clipboard)
|
||||||
{
|
{
|
||||||
|
auto& document = *manager.get();
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& selection = document.selectedSpritesheets;
|
auto& multiSelect = document.spritesheetMultiSelect;
|
||||||
|
auto& unused = document.unusedSpritesheetIDs;
|
||||||
if (document.is_just_changed(change::SPRITESHEETS)) unusedSpritesheetIDs = anm2.spritesheets_unused();
|
auto& hovered = document.hoveredSpritesheet;
|
||||||
|
auto& reference = document.referenceSpritesheet;
|
||||||
|
|
||||||
if (ImGui::Begin("Spritesheets", &settings.windowIsSpritesheets))
|
if (ImGui::Begin("Spritesheets", &settings.windowIsSpritesheets))
|
||||||
{
|
{
|
||||||
auto style = ImGui::GetStyle();
|
auto style = ImGui::GetStyle();
|
||||||
storage.user_data_set(&selection);
|
|
||||||
|
auto context_menu = [&]()
|
||||||
|
{
|
||||||
|
auto copy = [&]()
|
||||||
|
{
|
||||||
|
if (!multiSelect.empty())
|
||||||
|
{
|
||||||
|
std::string clipboardText{};
|
||||||
|
for (auto& id : multiSelect)
|
||||||
|
clipboardText += anm2.content.spritesheets[id].to_string(id);
|
||||||
|
clipboard.set(clipboardText);
|
||||||
|
}
|
||||||
|
else if (hovered > -1)
|
||||||
|
clipboard.set(anm2.content.spritesheets[hovered].to_string(hovered));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto paste = [&](merge::Type type)
|
||||||
|
{
|
||||||
|
auto clipboardText = clipboard.get();
|
||||||
|
document.spritesheets_deserialize(clipboardText, type);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
|
||||||
|
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste(merge::APPEND);
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||||
|
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
|
||||||
|
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(clipboard.is_empty());
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Paste"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||||
|
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar(2);
|
||||||
|
};
|
||||||
|
|
||||||
auto childSize = imgui::size_without_footer_get(2);
|
auto childSize = imgui::size_without_footer_get(2);
|
||||||
|
|
||||||
@@ -37,7 +95,7 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||||
|
|
||||||
storage.begin(anm2.content.spritesheets.size());
|
multiSelect.start(anm2.content.spritesheets.size());
|
||||||
|
|
||||||
for (auto& [id, spritesheet] : anm2.content.spritesheets)
|
for (auto& [id, spritesheet] : anm2.content.spritesheets)
|
||||||
{
|
{
|
||||||
@@ -45,15 +103,16 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
if (ImGui::BeginChild("##Spritesheet Child", spritesheetChildSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("##Spritesheet Child", spritesheetChildSize, ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
auto isSelected = selection.contains(id);
|
auto isSelected = multiSelect.contains(id);
|
||||||
auto isReferenced = id == document.referenceSpritesheet;
|
auto isReferenced = id == reference;
|
||||||
auto cursorPos = ImGui::GetCursorPos();
|
auto cursorPos = ImGui::GetCursorPos();
|
||||||
auto& texture = spritesheet.texture;
|
auto& texture = spritesheet.texture.is_valid() ? spritesheet.texture : resources.icons[icon::NONE];
|
||||||
|
auto path = spritesheet.path.empty() ? anm2::NO_PATH : spritesheet.path.c_str();
|
||||||
|
|
||||||
ImGui::SetNextItemSelectionUserData(id);
|
ImGui::SetNextItemSelectionUserData(id);
|
||||||
ImGui::SetNextItemStorageID(id);
|
ImGui::SetNextItemStorageID(id);
|
||||||
if (ImGui::Selectable("##Spritesheet Selectable", isSelected, 0, spritesheetChildSize))
|
if (ImGui::Selectable("##Spritesheet Selectable", isSelected, 0, spritesheetChildSize)) reference = id;
|
||||||
document.referenceSpritesheet = id;
|
if (ImGui::IsItemHovered()) hovered = id;
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||||
@@ -63,32 +122,31 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
auto viewport = ImGui::GetMainViewport();
|
auto viewport = ImGui::GetMainViewport();
|
||||||
|
|
||||||
auto size = texture.size.x * texture.size.y > (viewport->Size.x * viewport->Size.y) * 0.5f
|
auto textureSize = texture.size.x * texture.size.y > (viewport->Size.x * viewport->Size.y) * 0.5f
|
||||||
? to_vec2(viewport->Size) * 0.5f
|
? to_vec2(viewport->Size) * 0.5f
|
||||||
: vec2(texture.size);
|
: vec2(texture.size);
|
||||||
|
|
||||||
auto aspectRatio = (float)texture.size.x / texture.size.y;
|
auto aspectRatio = (float)texture.size.x / texture.size.y;
|
||||||
|
|
||||||
if (size.x / size.y > aspectRatio)
|
if (textureSize.x / textureSize.y > aspectRatio)
|
||||||
size.x = size.y * aspectRatio;
|
textureSize.x = textureSize.y * aspectRatio;
|
||||||
else
|
else
|
||||||
size.y = size.x / aspectRatio;
|
textureSize.y = textureSize.x / aspectRatio;
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Spritesheet Tooltip Image Child", to_imvec2(size), ImGuiChildFlags_Borders))
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||||
|
if (ImGui::BeginChild("##Spritesheet Tooltip Image Child", to_imvec2(textureSize),
|
||||||
|
ImGuiChildFlags_Borders))
|
||||||
ImGui::Image(texture.id, ImGui::GetContentRegionAvail());
|
ImGui::Image(texture.id, ImGui::GetContentRegionAvail());
|
||||||
|
ImGui::PopStyleVar();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (ImGui::BeginChild(
|
if (ImGui::BeginChild("##Spritesheet Info Tooltip Child"))
|
||||||
"##Spritesheet Info Tooltip Child",
|
|
||||||
ImVec2(ImGui::CalcTextSize(spritesheet.path.c_str()).x + ImGui::GetTextLineHeightWithSpacing(),
|
|
||||||
0)))
|
|
||||||
{
|
{
|
||||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||||
ImGui::TextUnformatted(spritesheet.path.c_str());
|
ImGui::TextUnformatted(path);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
ImGui::Text("ID: %d", id);
|
ImGui::Text("ID: %d", id);
|
||||||
ImGui::Text("Size: %d x %d", texture.size.x, texture.size.y);
|
ImGui::Text("Size: %d x %d", texture.size.x, texture.size.y);
|
||||||
@@ -97,7 +155,6 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
|
|
||||||
auto imageSize = to_imvec2(vec2(spritesheetChildSize.y));
|
auto imageSize = to_imvec2(vec2(spritesheetChildSize.y));
|
||||||
@@ -116,22 +173,24 @@ namespace anm2ed::spritesheets
|
|||||||
spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
|
spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
|
||||||
|
|
||||||
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
||||||
ImGui::Text(SPRITESHEET_FORMAT, id, spritesheet.path.c_str());
|
ImGui::Text(SPRITESHEET_FORMAT, id, path);
|
||||||
if (isReferenced) ImGui::PopFont();
|
if (isReferenced) ImGui::PopFont();
|
||||||
|
|
||||||
|
context_menu();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.end();
|
multiSelect.finish();
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar(2);
|
||||||
|
|
||||||
|
context_menu();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
|
|
||||||
auto rowOneWidgetSize = imgui::widget_size_with_row_get(4);
|
auto rowOneWidgetSize = imgui::widget_size_with_row_get(4);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
@@ -140,24 +199,21 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
if (dialog.is_selected_file(dialog::SPRITESHEET_OPEN))
|
if (dialog.is_selected_file(dialog::SPRITESHEET_OPEN))
|
||||||
{
|
{
|
||||||
int id{};
|
document.spritesheet_add(dialog.path);
|
||||||
anm2.spritesheet_add(document.directory_get(), dialog.path, id);
|
|
||||||
selection = {id};
|
|
||||||
document.change(change::SPRITESHEETS);
|
|
||||||
dialog.reset();
|
dialog.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.empty());
|
ImGui::BeginDisabled(multiSelect.empty());
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Reload", rowOneWidgetSize))
|
if (ImGui::Button("Reload", rowOneWidgetSize))
|
||||||
{
|
{
|
||||||
for (auto& id : selection)
|
for (auto& id : multiSelect)
|
||||||
{
|
{
|
||||||
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||||
spritesheet.reload(document.directory_get());
|
spritesheet.reload(document.directory_get());
|
||||||
toasts.add(std::format("Reloaded spritesheet #{}: {}", id, spritesheet.path.string()));
|
toasts.info(std::format("Reloaded spritesheet #{}: {}", id, spritesheet.path.string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("Reloads the selected spritesheets.");
|
ImGui::SetItemTooltip("Reloads the selected spritesheets.");
|
||||||
@@ -166,7 +222,7 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.size() != 1);
|
ImGui::BeginDisabled(multiSelect.size() != 1);
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Replace", rowOneWidgetSize)) dialog.spritesheet_replace();
|
if (ImGui::Button("Replace", rowOneWidgetSize)) dialog.spritesheet_replace();
|
||||||
ImGui::SetItemTooltip("Replace the selected spritesheet with a new one.");
|
ImGui::SetItemTooltip("Replace the selected spritesheet with a new one.");
|
||||||
@@ -175,27 +231,27 @@ namespace anm2ed::spritesheets
|
|||||||
|
|
||||||
if (dialog.is_selected_file(dialog::SPRITESHEET_REPLACE))
|
if (dialog.is_selected_file(dialog::SPRITESHEET_REPLACE))
|
||||||
{
|
{
|
||||||
auto& id = *selection.begin();
|
auto& id = *multiSelect.begin();
|
||||||
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||||
spritesheet = Spritesheet(document.directory_get(), dialog.path);
|
spritesheet = Spritesheet(document.directory_get(), dialog.path);
|
||||||
toasts.add(std::format("Replaced spritesheet #{}: {}", id, spritesheet.path.string()));
|
toasts.info(std::format("Replaced spritesheet #{}: {}", id, spritesheet.path.string()));
|
||||||
dialog.reset();
|
dialog.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(unusedSpritesheetIDs.empty());
|
ImGui::BeginDisabled(unused.empty());
|
||||||
{
|
{
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
if (ImGui::Button("Remove Unused", rowOneWidgetSize))
|
if (ImGui::Button("Remove Unused", rowOneWidgetSize))
|
||||||
{
|
{
|
||||||
for (auto& id : unusedSpritesheetIDs)
|
for (auto& id : unused)
|
||||||
{
|
{
|
||||||
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||||
toasts.add(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
|
toasts.info(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
|
||||||
anm2.spritesheet_remove(id);
|
anm2.spritesheet_remove(id);
|
||||||
}
|
}
|
||||||
unusedSpritesheetIDs.clear();
|
unused.clear();
|
||||||
document.change(change::SPRITESHEETS);
|
document.change(change::SPRITESHEETS);
|
||||||
}
|
}
|
||||||
imgui::set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
|
imgui::set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
|
||||||
@@ -206,11 +262,11 @@ namespace anm2ed::spritesheets
|
|||||||
auto rowTwoWidgetSize = imgui::widget_size_with_row_get(3);
|
auto rowTwoWidgetSize = imgui::widget_size_with_row_get(3);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutSelectAll);
|
imgui::shortcut(settings.shortcutSelectAll);
|
||||||
ImGui::BeginDisabled(selection.size() == anm2.content.spritesheets.size());
|
ImGui::BeginDisabled(multiSelect.size() == anm2.content.spritesheets.size());
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Select All", rowTwoWidgetSize))
|
if (ImGui::Button("Select All", rowTwoWidgetSize))
|
||||||
for (auto& id : anm2.content.spritesheets | std::views::keys)
|
for (auto& id : anm2.content.spritesheets | std::views::keys)
|
||||||
selection.insert(id);
|
multiSelect.insert(id);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
imgui::set_item_tooltip_shortcut("Select all spritesheets.", settings.shortcutSelectAll);
|
imgui::set_item_tooltip_shortcut("Select all spritesheets.", settings.shortcutSelectAll);
|
||||||
@@ -218,24 +274,24 @@ namespace anm2ed::spritesheets
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutSelectNone);
|
imgui::shortcut(settings.shortcutSelectNone);
|
||||||
ImGui::BeginDisabled(selection.empty());
|
ImGui::BeginDisabled(multiSelect.empty());
|
||||||
if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
|
if (ImGui::Button("Select None", rowTwoWidgetSize)) multiSelect.clear();
|
||||||
imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
|
imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(selection.empty());
|
ImGui::BeginDisabled(multiSelect.empty());
|
||||||
{
|
{
|
||||||
if (ImGui::Button("Save", rowTwoWidgetSize))
|
if (ImGui::Button("Save", rowTwoWidgetSize))
|
||||||
{
|
{
|
||||||
for (auto& id : selection)
|
for (auto& id : multiSelect)
|
||||||
{
|
{
|
||||||
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||||
if (spritesheet.save(document.directory_get()))
|
if (spritesheet.save(document.directory_get()))
|
||||||
toasts.add(std::format("Saved spritesheet #{}: {}", id, spritesheet.path.string()));
|
toasts.info(std::format("Saved spritesheet #{}: {}", id, spritesheet.path.string()));
|
||||||
else
|
else
|
||||||
toasts.add(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string()));
|
toasts.info(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "clipboard.h"
|
||||||
#include "dialog.h"
|
#include "dialog.h"
|
||||||
#include "document.h"
|
#include "manager.h"
|
||||||
#include "imgui.h"
|
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -10,11 +10,8 @@ namespace anm2ed::spritesheets
|
|||||||
{
|
{
|
||||||
class Spritesheets
|
class Spritesheets
|
||||||
{
|
{
|
||||||
imgui::MultiSelectStorage storage{};
|
|
||||||
std::set<int> unusedSpritesheetIDs{};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document::Document& document, settings::Settings& settings, resources::Resources& resources,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&, dialog::Dialog&,
|
||||||
dialog::Dialog& dialog);
|
clipboard::Clipboard& clipboard);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ namespace anm2ed::state
|
|||||||
{
|
{
|
||||||
if (auto document = manager.get())
|
if (auto document = manager.get())
|
||||||
if (auto animation = document->animation_get())
|
if (auto animation = document->animation_get())
|
||||||
if (playback.isPlaying)
|
if (document->playback.isPlaying)
|
||||||
playback.tick(document->anm2.info.fps, animation->frameNum, animation->isLoop || settings.playbackIsLoop);
|
document->playback.tick(document->anm2.info.fps, animation->frameNum,
|
||||||
|
animation->isLoop || settings.playbackIsLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void State::update(SDL_Window*& window, Settings& settings)
|
void State::update(SDL_Window*& window, Settings& settings)
|
||||||
@@ -44,9 +45,21 @@ namespace anm2ed::state
|
|||||||
switch (event.type)
|
switch (event.type)
|
||||||
{
|
{
|
||||||
case SDL_EVENT_DROP_FILE:
|
case SDL_EVENT_DROP_FILE:
|
||||||
if (auto droppedFile = event.drop.data; filesystem::path_is_extension(droppedFile, "anm2"))
|
{
|
||||||
|
auto droppedFile = event.drop.data;
|
||||||
|
if (filesystem::path_is_extension(droppedFile, "anm2"))
|
||||||
manager.open(std::string(droppedFile));
|
manager.open(std::string(droppedFile));
|
||||||
|
else if (filesystem::path_is_extension(droppedFile, "png"))
|
||||||
|
{
|
||||||
|
if (auto document = manager.get())
|
||||||
|
document->spritesheet_add(droppedFile);
|
||||||
|
else
|
||||||
|
toasts.warning(std::format("Could not open spritesheet: (open a document first!)", droppedFile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
toasts.warning(std::format("Could not parse file: {} (must be .anm2 or .png)", droppedFile));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
isQuit = true;
|
isQuit = true;
|
||||||
break;
|
break;
|
||||||
@@ -59,8 +72,8 @@ namespace anm2ed::state
|
|||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
taskbar.update(settings, dialog, manager, isQuit);
|
taskbar.update(manager, settings, dialog, isQuit);
|
||||||
dockspace.update(taskbar, documents, manager, settings, resources, dialog, playback);
|
dockspace.update(taskbar, documents, manager, settings, resources, dialog, clipboard);
|
||||||
toasts.update();
|
toasts.update();
|
||||||
|
|
||||||
documents.update(taskbar, manager, resources);
|
documents.update(taskbar, manager, resources);
|
||||||
|
|||||||
14
src/state.h
14
src/state.h
@@ -8,16 +8,16 @@ namespace anm2ed::state
|
|||||||
{
|
{
|
||||||
class State
|
class State
|
||||||
{
|
{
|
||||||
void tick(settings::Settings& settings);
|
void tick(settings::Settings&);
|
||||||
void update(SDL_Window*& window, settings::Settings& settings);
|
void update(SDL_Window*&, settings::Settings&);
|
||||||
void render(SDL_Window*& window, settings::Settings& settings);
|
void render(SDL_Window*&, settings::Settings&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool isQuit{};
|
bool isQuit{};
|
||||||
dialog::Dialog dialog;
|
dialog::Dialog dialog;
|
||||||
resources::Resources resources;
|
resources::Resources resources;
|
||||||
playback::Playback playback;
|
manager::Manager manager;
|
||||||
document_manager::DocumentManager manager;
|
clipboard::Clipboard clipboard;
|
||||||
|
|
||||||
taskbar::Taskbar taskbar;
|
taskbar::Taskbar taskbar;
|
||||||
documents::Documents documents;
|
documents::Documents documents;
|
||||||
@@ -26,8 +26,8 @@ namespace anm2ed::state
|
|||||||
uint64_t previousTick{};
|
uint64_t previousTick{};
|
||||||
uint64_t previousUpdate{};
|
uint64_t previousUpdate{};
|
||||||
|
|
||||||
State(SDL_Window*& window, std::vector<std::string>& arguments);
|
State(SDL_Window*&, std::vector<std::string>&);
|
||||||
|
|
||||||
void loop(SDL_Window*& window, settings::Settings& settings);
|
void loop(SDL_Window*&, settings::Settings&);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::dialog;
|
using namespace anm2ed::dialog;
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
|
|
||||||
namespace anm2ed::taskbar
|
namespace anm2ed::taskbar
|
||||||
{
|
{
|
||||||
void Taskbar::update(Settings& settings, Dialog& dialog, DocumentManager& manager, bool& isQuit)
|
void Taskbar::update(Manager& manager, Settings& settings, Dialog& dialog, bool& isQuit)
|
||||||
{
|
{
|
||||||
auto document = manager.get();
|
auto document = manager.get();
|
||||||
auto animation = document ? document->animation_get() : nullptr;
|
auto animation = document ? document->animation_get() : nullptr;
|
||||||
@@ -89,7 +89,11 @@ namespace anm2ed::taskbar
|
|||||||
|
|
||||||
if (ImGui::BeginMenu("Settings"))
|
if (ImGui::BeginMenu("Settings"))
|
||||||
{
|
{
|
||||||
if (ImGui::MenuItem("Configure")) configurePopup.open();
|
if (ImGui::MenuItem("Configure"))
|
||||||
|
{
|
||||||
|
editSettings = settings;
|
||||||
|
configurePopup.open();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "dialog.h"
|
#include "dialog.h"
|
||||||
#include "document_manager.h"
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "manager.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
namespace anm2ed::taskbar
|
namespace anm2ed::taskbar
|
||||||
@@ -12,12 +12,11 @@ namespace anm2ed::taskbar
|
|||||||
imgui::PopupHelper configurePopup{imgui::PopupHelper("Configure")};
|
imgui::PopupHelper configurePopup{imgui::PopupHelper("Configure")};
|
||||||
imgui::PopupHelper aboutPopup{imgui::PopupHelper("About")};
|
imgui::PopupHelper aboutPopup{imgui::PopupHelper("About")};
|
||||||
settings::Settings editSettings{};
|
settings::Settings editSettings{};
|
||||||
int selectedShortcut{};
|
int selectedShortcut{-1};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float height{};
|
float height{};
|
||||||
|
|
||||||
void update(settings::Settings& settings, dialog::Dialog& dialog, document_manager::DocumentManager& manager,
|
void update(manager::Manager&, settings::Settings&, dialog::Dialog&, bool&);
|
||||||
bool& isQuit);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace anm2ed::texture
|
|||||||
return id != 0;
|
return id != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::download(std::vector<uint8_t>& pixels)
|
void Texture::download()
|
||||||
{
|
{
|
||||||
pixels.resize(size.x * size.y * CHANNELS);
|
pixels.resize(size.x * size.y * CHANNELS);
|
||||||
glBindTexture(GL_TEXTURE_2D, id);
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
@@ -37,9 +37,20 @@ namespace anm2ed::texture
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::init(const uint8_t* data, bool isDownload)
|
void Texture::upload(const uint8_t* data)
|
||||||
{
|
{
|
||||||
glGenTextures(1, &id);
|
if (!data || size.x <= 0 || size.y <= 0) return;
|
||||||
|
|
||||||
|
const size_t pixelCount = static_cast<size_t>(size.x) * static_cast<size_t>(size.y) * CHANNELS;
|
||||||
|
pixels.assign(data, data + pixelCount);
|
||||||
|
upload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::upload()
|
||||||
|
{
|
||||||
|
if (pixels.empty() || size.x <= 0 || size.y <= 0) return;
|
||||||
|
|
||||||
|
if (!is_valid()) glGenTextures(1, &id);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, id);
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
|
||||||
@@ -48,12 +59,10 @@ namespace anm2ed::texture
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
if (isDownload) download(pixels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture() = default;
|
Texture::Texture() = default;
|
||||||
@@ -63,11 +72,31 @@ namespace anm2ed::texture
|
|||||||
if (is_valid()) glDeleteTextures(1, &id);
|
if (is_valid()) glDeleteTextures(1, &id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture::Texture(const Texture& other)
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
Texture::Texture(Texture&& other)
|
Texture::Texture(Texture&& other)
|
||||||
{
|
{
|
||||||
*this = std::move(other);
|
*this = std::move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Texture& Texture::operator=(const Texture& other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
if (is_valid()) glDeleteTextures(1, &id);
|
||||||
|
id = 0;
|
||||||
|
size = other.size;
|
||||||
|
filter = other.filter;
|
||||||
|
channels = other.channels;
|
||||||
|
pixels = other.pixels;
|
||||||
|
if (!pixels.empty()) upload();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Texture& Texture::operator=(Texture&& other)
|
Texture& Texture::operator=(Texture&& other)
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
@@ -94,24 +123,21 @@ namespace anm2ed::texture
|
|||||||
|
|
||||||
size = svgSize;
|
size = svgSize;
|
||||||
filter = GL_LINEAR;
|
filter = GL_LINEAR;
|
||||||
init(bitmap.data());
|
upload(bitmap.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::Texture(const std::string& pngPath, bool isDownload)
|
Texture::Texture(const std::string& pngPath)
|
||||||
{
|
{
|
||||||
if (const uint8* data = stbi_load(pngPath.c_str(), &size.x, &size.y, nullptr, CHANNELS); data)
|
if (const uint8* data = stbi_load(pngPath.c_str(), &size.x, &size.y, nullptr, CHANNELS); data)
|
||||||
{
|
{
|
||||||
init(data, isDownload);
|
upload(data);
|
||||||
stbi_image_free((void*)data);
|
stbi_image_free((void*)data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Texture::write_png(const std::string& path)
|
bool Texture::write_png(const std::string& path)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> pixels;
|
return stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, this->pixels.data(), size.x * CHANNELS);
|
||||||
download(pixels);
|
|
||||||
const bool isSuccess = stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, pixels.data(), size.x * CHANNELS);
|
|
||||||
return isSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Texture::bind(GLuint unit)
|
void Texture::bind(GLuint unit)
|
||||||
|
|||||||
@@ -20,17 +20,20 @@ namespace anm2ed::texture
|
|||||||
std::vector<uint8_t> pixels{};
|
std::vector<uint8_t> pixels{};
|
||||||
|
|
||||||
bool is_valid();
|
bool is_valid();
|
||||||
void download(std::vector<uint8_t>& pixels);
|
void download();
|
||||||
void init(const uint8_t* data, bool isDownload = false);
|
void upload(const uint8_t*);
|
||||||
|
void upload();
|
||||||
Texture();
|
Texture();
|
||||||
|
|
||||||
~Texture();
|
~Texture();
|
||||||
Texture(Texture&& other);
|
Texture(const Texture&);
|
||||||
Texture& operator=(Texture&& other);
|
Texture(Texture&&);
|
||||||
Texture(const char* svgData, size_t svgDataLength, glm::ivec2 svgSize);
|
Texture& operator=(const Texture&);
|
||||||
Texture(const std::string& pngPath, bool isDownload = false);
|
Texture& operator=(Texture&&);
|
||||||
bool write_png(const std::string& path);
|
Texture(const char*, size_t, glm::ivec2);
|
||||||
void bind(GLuint unit = 0);
|
Texture(const std::string&);
|
||||||
void unbind(GLuint unit = 0);
|
bool write_png(const std::string&);
|
||||||
|
void bind(GLuint = 0);
|
||||||
|
void unbind(GLuint = 0);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
166
src/timeline.cpp
166
src/timeline.cpp
@@ -7,7 +7,7 @@
|
|||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
using namespace anm2ed::document_manager;
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
using namespace anm2ed::playback;
|
using namespace anm2ed::playback;
|
||||||
@@ -50,9 +50,12 @@ namespace anm2ed::timeline
|
|||||||
- Press {} to shorten the selected frame, by one frame.
|
- Press {} to shorten the selected frame, by one frame.
|
||||||
- Hold Alt while clicking a non-trigger frame to toggle interpolation.)";
|
- Hold Alt while clicking a non-trigger frame to toggle interpolation.)";
|
||||||
|
|
||||||
void Timeline::item_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation,
|
void Timeline::item_child(Manager& manager, Document& document, anm2::Animation* animation, Settings& settings,
|
||||||
Settings& settings, Resources& resources, anm2::Type type, int id, int& index)
|
Resources& resources, anm2::Type type, int id, int& index)
|
||||||
{
|
{
|
||||||
|
auto& anm2 = document.anm2;
|
||||||
|
auto& reference = document.reference;
|
||||||
|
|
||||||
auto item = animation ? animation->item_get(type, id) : nullptr;
|
auto item = animation ? animation->item_get(type, id) : nullptr;
|
||||||
auto isVisible = item ? item->isVisible : false;
|
auto isVisible = item ? item->isVisible : false;
|
||||||
auto isActive = reference.itemType == type && reference.itemID == id;
|
auto isActive = reference.itemType == type && reference.itemID == id;
|
||||||
@@ -102,7 +105,24 @@ namespace anm2ed::timeline
|
|||||||
{
|
{
|
||||||
anm2::Reference itemReference = {reference.animationIndex, type, id};
|
anm2::Reference itemReference = {reference.animationIndex, type, id};
|
||||||
|
|
||||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) reference = itemReference;
|
if (ImGui::IsWindowHovered())
|
||||||
|
{
|
||||||
|
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case anm2::LAYER:
|
||||||
|
manager.layer_properties_open(id); // Handled in layers.cpp
|
||||||
|
break;
|
||||||
|
case anm2::NULL_:
|
||||||
|
manager.null_properties_open(id); // Handled in layers.cpp
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) reference = itemReference;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Image(resources.icons[icon].id, imgui::icon_size_get());
|
ImGui::Image(resources.icons[icon].id, imgui::icon_size_get());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
@@ -121,7 +141,7 @@ namespace anm2ed::timeline
|
|||||||
int visibleIcon = isVisible ? icon::VISIBLE : icon::INVISIBLE;
|
int visibleIcon = isVisible ? icon::VISIBLE : icon::INVISIBLE;
|
||||||
|
|
||||||
if (ImGui::ImageButton("##Visible Toggle", resources.icons[visibleIcon].id, imgui::icon_size_get()))
|
if (ImGui::ImageButton("##Visible Toggle", resources.icons[visibleIcon].id, imgui::icon_size_get()))
|
||||||
isVisible = !isVisible;
|
document.item_visible_toggle(item);
|
||||||
ImGui::SetItemTooltip(isVisible ? "The item is shown. Press to hide." : "The item is hidden. Press to show.");
|
ImGui::SetItemTooltip(isVisible ? "The item is shown. Press to hide." : "The item is hidden. Press to show.");
|
||||||
|
|
||||||
if (type == anm2::NULL_)
|
if (type == anm2::NULL_)
|
||||||
@@ -134,7 +154,7 @@ namespace anm2ed::timeline
|
|||||||
ImVec2(itemSize.x - (ImGui::GetTextLineHeightWithSpacing() * 2) - ImGui::GetStyle().ItemSpacing.x,
|
ImVec2(itemSize.x - (ImGui::GetTextLineHeightWithSpacing() * 2) - ImGui::GetStyle().ItemSpacing.x,
|
||||||
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
|
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
|
||||||
if (ImGui::ImageButton("##Rect Toggle", resources.icons[rectIcon].id, imgui::icon_size_get()))
|
if (ImGui::ImageButton("##Rect Toggle", resources.icons[rectIcon].id, imgui::icon_size_get()))
|
||||||
isShowRect = !isShowRect;
|
document.null_rect_toggle(null);
|
||||||
ImGui::SetItemTooltip(isShowRect ? "The null's rect is shown. Press to hide."
|
ImGui::SetItemTooltip(isShowRect ? "The null's rect is shown. Press to hide."
|
||||||
: "The null's rect is hidden. Press to show.");
|
: "The null's rect is hidden. Press to show.");
|
||||||
}
|
}
|
||||||
@@ -180,9 +200,9 @@ namespace anm2ed::timeline
|
|||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::items_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources)
|
void Timeline::items_child(Manager& manager, Document& document, anm2::Animation* animation, Settings& settings,
|
||||||
|
Resources& resources)
|
||||||
{
|
{
|
||||||
auto& anm2 = document.anm2;
|
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
|
|
||||||
auto itemsChildSize = ImVec2(ImGui::GetTextLineHeightWithSpacing() * 15, ImGui::GetContentRegionAvail().y);
|
auto itemsChildSize = ImVec2(ImGui::GetTextLineHeightWithSpacing() * 15, ImGui::GetContentRegionAvail().y);
|
||||||
@@ -212,7 +232,7 @@ namespace anm2ed::timeline
|
|||||||
{
|
{
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
item_child(anm2, reference, animation, settings, resources, type, id, index);
|
item_child(manager, document, animation, settings, resources, type, id, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
item_child_row(anm2::NONE);
|
item_child_row(anm2::NONE);
|
||||||
@@ -253,20 +273,24 @@ namespace anm2ed::timeline
|
|||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||||
|
|
||||||
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + style.WindowPadding.x, ImGui::GetCursorPosY()));
|
||||||
|
auto widgetSize = imgui::widget_size_with_row_get(2, ImGui::GetContentRegionAvail().x - style.WindowPadding.x);
|
||||||
|
|
||||||
ImGui::BeginDisabled(!animation);
|
ImGui::BeginDisabled(!animation);
|
||||||
{
|
{
|
||||||
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
if (ImGui::Button("Add", widgetSize)) propertiesPopup.open();
|
if (ImGui::Button("Add", widgetSize)) propertiesPopup.open();
|
||||||
ImGui::SetItemTooltip("%s", "Add a new item to the animation.");
|
imgui::set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(document.item_get());
|
ImGui::BeginDisabled(!document.item_get() && reference.itemType != anm2::LAYER &&
|
||||||
|
reference.itemType != anm2::NULL_);
|
||||||
{
|
{
|
||||||
ImGui::Button("Remove", widgetSize);
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
ImGui::SetItemTooltip("%s", "Remove the selected items from the animation.");
|
if (ImGui::Button("Remove", widgetSize)) document.item_remove(animation);
|
||||||
|
imgui::set_item_tooltip_shortcut("Remove the selected items from the animation.", settings.shortcutRemove);
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
@@ -278,9 +302,10 @@ namespace anm2ed::timeline
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::frame_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources,
|
void Timeline::frame_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources,
|
||||||
Playback& playback, anm2::Type type, int id, int& index, float width)
|
anm2::Type type, int id, int& index, float width)
|
||||||
{
|
{
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
|
auto& playback = document.playback;
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
auto item = animation ? animation->item_get(type, id) : nullptr;
|
auto item = animation ? animation->item_get(type, id) : nullptr;
|
||||||
auto isVisible = item ? item->isVisible : false;
|
auto isVisible = item ? item->isVisible : false;
|
||||||
@@ -455,7 +480,11 @@ namespace anm2ed::timeline
|
|||||||
if (ImGui::Button("##Frame Button", size))
|
if (ImGui::Button("##Frame Button", size))
|
||||||
{
|
{
|
||||||
if (type != anm2::TRIGGER && ImGui::IsKeyDown(ImGuiMod_Alt)) frame.isInterpolated = !frame.isInterpolated;
|
if (type != anm2::TRIGGER && ImGui::IsKeyDown(ImGuiMod_Alt)) frame.isInterpolated = !frame.isInterpolated;
|
||||||
if (type == anm2::LAYER) document.referenceSpritesheet = anm2.content.layers[id].spritesheetID;
|
if (type == anm2::LAYER)
|
||||||
|
{
|
||||||
|
document.referenceSpritesheet = anm2.content.layers[id].spritesheetID;
|
||||||
|
document.layersMultiSelect = {id};
|
||||||
|
}
|
||||||
reference = frameReference;
|
reference = frameReference;
|
||||||
reference.frameTime = frameTime;
|
reference.frameTime = frameTime;
|
||||||
}
|
}
|
||||||
@@ -481,10 +510,10 @@ namespace anm2ed::timeline
|
|||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::frames_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources,
|
void Timeline::frames_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources)
|
||||||
Playback& playback)
|
|
||||||
{
|
{
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
|
auto& playback = document.playback;
|
||||||
|
|
||||||
auto itemsChildWidth = ImGui::GetTextLineHeightWithSpacing() * 15;
|
auto itemsChildWidth = ImGui::GetTextLineHeightWithSpacing() * 15;
|
||||||
|
|
||||||
@@ -541,7 +570,7 @@ namespace anm2ed::timeline
|
|||||||
{
|
{
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
frame_child(document, animation, settings, resources, playback, type, id, index, childWidth);
|
frame_child(document, animation, settings, resources, type, id, index, childWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
frames_child_row(anm2::NONE);
|
frames_child_row(anm2::NONE);
|
||||||
@@ -614,25 +643,35 @@ namespace anm2ed::timeline
|
|||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutAdd);
|
auto item = document.item_get();
|
||||||
ImGui::Button("Insert Frame", widgetSize);
|
|
||||||
imgui::set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd);
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::BeginDisabled(!item);
|
||||||
|
{
|
||||||
|
imgui::shortcut(settings.shortcutAdd);
|
||||||
|
if (ImGui::Button("Insert Frame", widgetSize)) document.frames_add(item);
|
||||||
|
imgui::set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd);
|
||||||
|
|
||||||
imgui::shortcut(settings.shortcutRemove);
|
ImGui::SameLine();
|
||||||
ImGui::Button("Delete Frame", widgetSize);
|
|
||||||
imgui::set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::BeginDisabled(!document.frame_get());
|
||||||
|
{
|
||||||
|
imgui::shortcut(settings.shortcutRemove);
|
||||||
|
if (ImGui::Button("Delete Frame", widgetSize)) document.frames_delete(item);
|
||||||
|
imgui::set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
|
||||||
|
|
||||||
ImGui::Button("Bake", widgetSize);
|
ImGui::SameLine();
|
||||||
ImGui::SetItemTooltip("%s", "Turn interpolated frames into uninterpolated ones.");
|
|
||||||
|
if (ImGui::Button("Bake", widgetSize)) bakePopup.open();
|
||||||
|
ImGui::SetItemTooltip("Turn interpolated frames into uninterpolated ones.");
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (ImGui::Button("Fit Animation Length", widgetSize)) animation->frameNum = animation->length();
|
if (ImGui::Button("Fit Animation Length", widgetSize)) animation->frameNum = animation->length();
|
||||||
ImGui::SetItemTooltip("%s", "The animation length will be set to the effective length of the animation.");
|
ImGui::SetItemTooltip("The animation length will be set to the effective length of the animation.");
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
@@ -640,13 +679,13 @@ namespace anm2ed::timeline
|
|||||||
ImGui::InputInt("Animation Length", animation ? &animation->frameNum : &dummy_value<int>(), step::NORMAL,
|
ImGui::InputInt("Animation Length", animation ? &animation->frameNum : &dummy_value<int>(), step::NORMAL,
|
||||||
step::FAST, !animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0);
|
step::FAST, !animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0);
|
||||||
if (animation) animation->frameNum = clamp(animation->frameNum, anm2::FRAME_NUM_MIN, anm2::FRAME_NUM_MAX);
|
if (animation) animation->frameNum = clamp(animation->frameNum, anm2::FRAME_NUM_MIN, anm2::FRAME_NUM_MAX);
|
||||||
ImGui::SetItemTooltip("%s", "Set the animation's length.");
|
ImGui::SetItemTooltip("Set the animation's length.");
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(widgetSize.x);
|
ImGui::SetNextItemWidth(widgetSize.x);
|
||||||
ImGui::Checkbox("Loop", animation ? &animation->isLoop : &dummy_value<bool>());
|
ImGui::Checkbox("Loop", animation ? &animation->isLoop : &dummy_value<bool>());
|
||||||
ImGui::SetItemTooltip("%s", "Toggle the animation looping.");
|
ImGui::SetItemTooltip("Toggle the animation looping.");
|
||||||
}
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
@@ -655,13 +694,13 @@ namespace anm2ed::timeline
|
|||||||
ImGui::SetNextItemWidth(widgetSize.x);
|
ImGui::SetNextItemWidth(widgetSize.x);
|
||||||
ImGui::InputInt("FPS", &anm2.info.fps, 1, 5);
|
ImGui::InputInt("FPS", &anm2.info.fps, 1, 5);
|
||||||
anm2.info.fps = clamp(anm2.info.fps, anm2::FPS_MIN, anm2::FPS_MAX);
|
anm2.info.fps = clamp(anm2.info.fps, anm2::FPS_MIN, anm2::FPS_MAX);
|
||||||
ImGui::SetItemTooltip("%s", "Set the FPS of all animations.");
|
ImGui::SetItemTooltip("Set the FPS of all animations.");
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(widgetSize.x);
|
ImGui::SetNextItemWidth(widgetSize.x);
|
||||||
imgui::input_text_string("Author", &anm2.info.createdBy);
|
imgui::input_text_string("Author", &anm2.info.createdBy);
|
||||||
ImGui::SetItemTooltip("%s", "Set the author of the document.");
|
ImGui::SetItemTooltip("Set the author of the document.");
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
@@ -710,7 +749,6 @@ namespace anm2ed::timeline
|
|||||||
auto optionsSize = imgui::child_size_get(11);
|
auto optionsSize = imgui::child_size_get(11);
|
||||||
auto itemsSize = ImVec2(0, ImGui::GetContentRegionAvail().y -
|
auto itemsSize = ImVec2(0, ImGui::GetContentRegionAvail().y -
|
||||||
(optionsSize.y + footerSize.y + ImGui::GetStyle().ItemSpacing.y * 4));
|
(optionsSize.y + footerSize.y + ImGui::GetStyle().ItemSpacing.y * 4));
|
||||||
|
|
||||||
if (ImGui::BeginChild("Options", optionsSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("Options", optionsSize, ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
ImGui::SeparatorText("Type");
|
ImGui::SeparatorText("Type");
|
||||||
@@ -827,17 +865,7 @@ namespace anm2ed::timeline
|
|||||||
|
|
||||||
if (ImGui::Button("Add", widgetSize))
|
if (ImGui::Button("Add", widgetSize))
|
||||||
{
|
{
|
||||||
anm2::Reference addReference;
|
document.item_add((anm2::Type)type, addItemID, addItemName, (locale::Type)locale, addItemSpritesheetID);
|
||||||
|
|
||||||
if (type == anm2::LAYER)
|
|
||||||
addReference = anm2.layer_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName,
|
|
||||||
addItemSpritesheetID, (locale::Type)locale);
|
|
||||||
else if (type == anm2::NULL_)
|
|
||||||
addReference =
|
|
||||||
anm2.null_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName, (locale::Type)locale);
|
|
||||||
|
|
||||||
reference = addReference;
|
|
||||||
|
|
||||||
item_properties_close();
|
item_properties_close();
|
||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("Add the item, with the settings specified.");
|
ImGui::SetItemTooltip("Add the item, with the settings specified.");
|
||||||
@@ -849,11 +877,49 @@ namespace anm2ed::timeline
|
|||||||
|
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bakePopup.trigger();
|
||||||
|
|
||||||
|
if (ImGui::BeginPopupModal(bakePopup.label, &bakePopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||||
|
{
|
||||||
|
auto& interval = settings.bakeInterval;
|
||||||
|
auto& isRoundRotation = settings.bakeIsRoundRotation;
|
||||||
|
auto& isRoundScale = settings.bakeIsRoundScale;
|
||||||
|
|
||||||
|
auto frame = document.frame_get();
|
||||||
|
|
||||||
|
ImGui::InputInt("Interval", &interval, step::NORMAL, step::FAST);
|
||||||
|
ImGui::SetItemTooltip("Set the maximum delay of each frame that will be baked.");
|
||||||
|
interval = glm::clamp(interval, anm2::FRAME_DELAY_MIN, frame ? frame->delay : anm2::FRAME_DELAY_MIN);
|
||||||
|
|
||||||
|
ImGui::Checkbox("Round Rotation", &isRoundRotation);
|
||||||
|
ImGui::SetItemTooltip("Rotation will be rounded to the nearest whole number.");
|
||||||
|
|
||||||
|
ImGui::Checkbox("Round Scale", &isRoundScale);
|
||||||
|
ImGui::SetItemTooltip("Scale will be rounded to the nearest whole number.");
|
||||||
|
|
||||||
|
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||||
|
|
||||||
|
if (ImGui::Button("Bake", widgetSize))
|
||||||
|
{
|
||||||
|
document.frames_bake(interval, isRoundScale, isRoundRotation);
|
||||||
|
bakePopup.close();
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Bake the selected frame(s) with the options selected.");
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button("Cancel", widgetSize)) bakePopup.close();
|
||||||
|
ImGui::SetItemTooltip("Cancel baking frames.");
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::update(DocumentManager& manager, Settings& settings, Resources& resources, Playback& playback)
|
void Timeline::update(Manager& manager, Settings& settings, Resources& resources)
|
||||||
{
|
{
|
||||||
auto& document = *manager.get();
|
auto& document = *manager.get();
|
||||||
|
auto& playback = document.playback;
|
||||||
auto& anm2 = document.anm2;
|
auto& anm2 = document.anm2;
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
auto animation = document.animation_get();
|
auto animation = document.animation_get();
|
||||||
@@ -864,8 +930,8 @@ namespace anm2ed::timeline
|
|||||||
if (ImGui::Begin("Timeline", &settings.windowIsTimeline))
|
if (ImGui::Begin("Timeline", &settings.windowIsTimeline))
|
||||||
{
|
{
|
||||||
isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
|
isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
|
||||||
frames_child(document, animation, settings, resources, playback);
|
frames_child(document, animation, settings, resources);
|
||||||
items_child(document, animation, settings, resources);
|
items_child(manager, document, animation, settings, resources);
|
||||||
}
|
}
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
#include "anm2.h"
|
#include "anm2.h"
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
#include "document_manager.h"
|
#include "manager.h"
|
||||||
#include "imgui.h"
|
|
||||||
#include "playback.h"
|
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -16,6 +14,7 @@ namespace anm2ed::timeline
|
|||||||
bool isWindowHovered{};
|
bool isWindowHovered{};
|
||||||
bool isHorizontalScroll{};
|
bool isHorizontalScroll{};
|
||||||
imgui::PopupHelper propertiesPopup{imgui::PopupHelper("Item Properties")};
|
imgui::PopupHelper propertiesPopup{imgui::PopupHelper("Item Properties")};
|
||||||
|
imgui::PopupHelper bakePopup{imgui::PopupHelper("Bake", imgui::POPUP_SMALL, true)};
|
||||||
std::string addItemName{};
|
std::string addItemName{};
|
||||||
int addItemSpritesheetID{};
|
int addItemSpritesheetID{};
|
||||||
bool addItemIsRect{};
|
bool addItemIsRect{};
|
||||||
@@ -26,20 +25,16 @@ namespace anm2ed::timeline
|
|||||||
ImDrawList* pickerLineDrawList{};
|
ImDrawList* pickerLineDrawList{};
|
||||||
ImGuiStyle style{};
|
ImGuiStyle style{};
|
||||||
|
|
||||||
void item_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation,
|
void item_child(manager::Manager&, Document&, anm2::Animation*, settings::Settings&, resources::Resources&,
|
||||||
settings::Settings& settings, resources::Resources& resources, anm2::Type type, int id, int& index);
|
anm2::Type, int, int&);
|
||||||
void items_child(Document& document, anm2::Animation* animation, settings::Settings& settings,
|
void items_child(manager::Manager&, Document&, anm2::Animation*, settings::Settings&, resources::Resources&);
|
||||||
resources::Resources& resources);
|
void frame_child(document::Document&, anm2::Animation*, settings::Settings&, resources::Resources&, anm2::Type, int,
|
||||||
void frame_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings,
|
int&, float);
|
||||||
resources::Resources& resources, playback::Playback& playback, anm2::Type type, int id, int& index,
|
void frames_child(document::Document&, anm2::Animation*, settings::Settings&, resources::Resources&);
|
||||||
float width);
|
|
||||||
void frames_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings,
|
|
||||||
resources::Resources& resources, playback::Playback& playback);
|
|
||||||
|
|
||||||
void popups(document::Document& document, anm2::Animation* animation, settings::Settings& settings);
|
void popups(document::Document&, anm2::Animation*, settings::Settings&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
|
void update(manager::Manager&, settings::Settings&, resources::Resources&);
|
||||||
resources::Resources& resources, playback::Playback& playback);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -64,17 +64,23 @@ namespace anm2ed::toast
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toasts::add(const std::string& message)
|
void Toasts::info(const std::string& message)
|
||||||
{
|
{
|
||||||
toasts.emplace_back(Toast(message));
|
toasts.emplace_back(Toast(message));
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toasts::add_error(const std::string& message)
|
void Toasts::error(const std::string& message)
|
||||||
{
|
{
|
||||||
toasts.emplace_back(Toast(message));
|
toasts.emplace_back(Toast(message));
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Toasts::warning(const std::string& message)
|
||||||
|
{
|
||||||
|
toasts.emplace_back(Toast(message));
|
||||||
|
logger.warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
Toasts toasts;
|
Toasts toasts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace anm2ed::toast
|
|||||||
std::string message{};
|
std::string message{};
|
||||||
float lifetime{};
|
float lifetime{};
|
||||||
|
|
||||||
Toast(const std::string& message);
|
Toast(const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Toasts
|
class Toasts
|
||||||
@@ -21,8 +21,9 @@ namespace anm2ed::toast
|
|||||||
std::vector<Toast> toasts{};
|
std::vector<Toast> toasts{};
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void add(const std::string& message);
|
void info(const std::string&);
|
||||||
void add_error(const std::string& message);
|
void error(const std::string&);
|
||||||
|
void warning(const std::string&);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Toasts toasts;
|
extern Toasts toasts;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
using namespace anm2ed::settings;
|
using namespace anm2ed::settings;
|
||||||
|
using namespace anm2ed::manager;
|
||||||
using namespace anm2ed::resources;
|
using namespace anm2ed::resources;
|
||||||
using namespace anm2ed::types;
|
using namespace anm2ed::types;
|
||||||
using namespace glm;
|
using namespace glm;
|
||||||
@@ -16,8 +17,10 @@ namespace anm2ed::tools
|
|||||||
{
|
{
|
||||||
constexpr auto COLOR_EDIT_LABEL = "##Color Edit";
|
constexpr auto COLOR_EDIT_LABEL = "##Color Edit";
|
||||||
|
|
||||||
void Tools::update(Settings& settings, Resources& resources)
|
void Tools::update(Manager& manager, Settings& settings, Resources& resources)
|
||||||
{
|
{
|
||||||
|
auto& document = *manager.get();
|
||||||
|
|
||||||
if (ImGui::Begin("Tools", &settings.windowIsTools))
|
if (ImGui::Begin("Tools", &settings.windowIsTools))
|
||||||
{
|
{
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));
|
||||||
@@ -31,8 +34,10 @@ namespace anm2ed::tools
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case tool::UNDO:
|
case tool::UNDO:
|
||||||
|
if (document.is_undo()) document.undo();
|
||||||
break;
|
break;
|
||||||
case tool::REDO:
|
case tool::REDO:
|
||||||
|
if (document.is_redo()) document.redo();
|
||||||
break;
|
break;
|
||||||
case tool::COLOR:
|
case tool::COLOR:
|
||||||
if (ImGui::IsPopupOpen(COLOR_EDIT_LABEL))
|
if (ImGui::IsPopupOpen(COLOR_EDIT_LABEL))
|
||||||
@@ -68,8 +73,12 @@ namespace anm2ed::tools
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (i == tool::UNDO) ImGui::BeginDisabled(!document.is_undo());
|
||||||
|
if (i == tool::REDO) ImGui::BeginDisabled(!document.is_redo());
|
||||||
if (ImGui::ImageButton(info.label, resources.icons[info.icon].id, to_imvec2(size)))
|
if (ImGui::ImageButton(info.label, resources.icons[info.icon].id, to_imvec2(size)))
|
||||||
tool_switch((tool::Type)i);
|
tool_switch((tool::Type)i);
|
||||||
|
if (i == tool::UNDO) ImGui::EndDisabled();
|
||||||
|
if (i == tool::REDO) ImGui::EndDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto widthIncrement = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
auto widthIncrement = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
#include "resources.h"
|
#include "resources.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
@@ -11,6 +12,6 @@ namespace anm2ed::tools
|
|||||||
ImVec2 colorEditPosition{};
|
ImVec2 colorEditPosition{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void update(settings::Settings& settings, resources::Resources& resources);
|
void update(manager::Manager&, settings::Settings&, resources::Resources&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/types.h
10
src/types.h
@@ -15,10 +15,20 @@ namespace anm2ed::types::change
|
|||||||
ANIMATIONS,
|
ANIMATIONS,
|
||||||
ITEMS,
|
ITEMS,
|
||||||
FRAMES,
|
FRAMES,
|
||||||
|
ALL,
|
||||||
COUNT
|
COUNT
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace anm2ed::types::draw_order
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
BELOW,
|
||||||
|
ABOVE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace anm2ed::types::shortcut
|
namespace anm2ed::types::shortcut
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type
|
||||||
|
|||||||
51
src/util.h
51
src/util.h
@@ -1,20 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <ranges>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace anm2ed::util::time
|
namespace anm2ed::util::time
|
||||||
{
|
{
|
||||||
std::string get(const char* format);
|
std::string get(const char*);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace anm2ed::util::string
|
namespace anm2ed::util::string
|
||||||
{
|
{
|
||||||
std::string to_lower(const std::string& string);
|
std::string to_lower(const std::string&);
|
||||||
std::string replace_backslash(const std::string& string);
|
std::string replace_backslash(const std::string&);
|
||||||
bool to_bool(const std::string& string);
|
bool to_bool(const std::string&);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace anm2ed::util::map
|
namespace anm2ed::util::map
|
||||||
@@ -52,4 +55,44 @@ namespace anm2ed::util::vector
|
|||||||
{
|
{
|
||||||
return index >= 0 && index < (int)v.size() ? &v[index] : nullptr;
|
return index >= 0 && index < (int)v.size() ? &v[index] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> std::set<int> move_indices(std::vector<T>& v, std::vector<int>& indices, int index)
|
||||||
|
{
|
||||||
|
if (indices.empty()) return {};
|
||||||
|
|
||||||
|
std::vector<int> sorted = indices;
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
sorted.erase(std::unique(sorted.begin(), sorted.end()), sorted.end());
|
||||||
|
|
||||||
|
// Determine if we are dragging items from below the target (insert before) or above (insert after)
|
||||||
|
bool insertAfter = !sorted.empty() && sorted.front() <= index;
|
||||||
|
|
||||||
|
std::vector<T> moveItems;
|
||||||
|
moveItems.reserve(sorted.size());
|
||||||
|
for (int i : sorted)
|
||||||
|
moveItems.push_back(std::move(v[i]));
|
||||||
|
|
||||||
|
for (auto i : sorted | std::views::reverse)
|
||||||
|
v.erase(v.begin() + i);
|
||||||
|
|
||||||
|
int originalSize = (int)v.size() + (int)sorted.size();
|
||||||
|
int reference = insertAfter ? index + 1 : index;
|
||||||
|
reference = std::clamp(reference, 0, originalSize);
|
||||||
|
|
||||||
|
int removedBeforeReference = 0;
|
||||||
|
for (int i : sorted)
|
||||||
|
if (i < reference) ++removedBeforeReference;
|
||||||
|
|
||||||
|
int insertPos = reference - removedBeforeReference;
|
||||||
|
insertPos = std::clamp(insertPos, 0, (int)v.size());
|
||||||
|
|
||||||
|
v.insert(v.begin() + insertPos, std::make_move_iterator(moveItems.begin()),
|
||||||
|
std::make_move_iterator(moveItems.end()));
|
||||||
|
|
||||||
|
std::set<int> moveIndices{};
|
||||||
|
for (int i = 0; i < (int)moveItems.size(); i++)
|
||||||
|
moveIndices.insert(insertPos + i);
|
||||||
|
|
||||||
|
return moveIndices;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,8 @@
|
|||||||
|
|
||||||
namespace anm2ed::xml
|
namespace anm2ed::xml
|
||||||
{
|
{
|
||||||
std::string document_to_string(tinyxml2::XMLDocument& self);
|
std::string document_to_string(tinyxml2::XMLDocument&);
|
||||||
tinyxml2::XMLError query_string_attribute(tinyxml2::XMLElement* element, const char* attribute, std::string* out);
|
tinyxml2::XMLError query_string_attribute(tinyxml2::XMLElement*, const char*, std::string*);
|
||||||
tinyxml2::XMLError query_path_attribute(tinyxml2::XMLElement* element, const char* attribute,
|
tinyxml2::XMLError query_path_attribute(tinyxml2::XMLElement*, const char*, std::filesystem::path*);
|
||||||
std::filesystem::path* out);
|
void query_color_attribute(tinyxml2::XMLElement*, const char*, float&);
|
||||||
void query_color_attribute(tinyxml2::XMLElement* element, const char* attribute, float& out);
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user