Refactoring, FFmpeg updates

This commit is contained in:
2025-11-03 00:16:05 -05:00
parent 62cd94ca78
commit 1e35910b0a
65 changed files with 2322 additions and 2236 deletions

View File

@@ -32,6 +32,10 @@ namespace anm2ed::imgui
{
auto& anm2 = document.anm2;
auto& playback = document.playback;
auto& zoom = document.previewZoom;
auto& pan = document.previewPan;
auto& isRootTransform = settings.previewIsRootTransform;
auto& scale = settings.renderScale;
if (playback.isPlaying)
{
@@ -41,18 +45,52 @@ namespace anm2ed::imgui
if (isSound && !anm2.content.sounds.empty())
if (auto animation = document.animation_get(); animation)
if (animation->triggers.isVisible && !isOnlyShowLayers)
if (auto trigger = animation->triggers.frame_generate(playback.time, anm2::TRIGGER); trigger.isVisible)
anm2.content.sounds[anm2.content.events[trigger.eventID].soundID].audio.play();
if (auto trigger = animation->triggers.frame_generate(playback.time, anm2::TRIGGER);
trigger.is_visible(anm2::TRIGGER))
if (anm2.content.sounds.contains(trigger.soundID)) anm2.content.sounds[trigger.soundID].audio.play();
document.reference.frameTime = playback.time;
}
if (manager.isRecording)
{
if (manager.isRecordingStart)
{
if (settings.renderIsRawAnimation)
{
savedSettings = settings;
settings.previewBackgroundColor = vec4();
settings.previewIsGrid = false;
settings.previewIsAxes = false;
settings.timelineIsOnlyShowLayers = true;
savedZoom = zoom;
savedPan = pan;
if (auto animation = document.animation_get())
{
auto rect = animation->rect(isRootTransform);
size = vec2(rect.z, rect.w) * scale;
set_to_rect(zoom, pan, rect);
}
isSizeTrySet = false;
bind();
viewport_set();
clear(settings.previewBackgroundColor);
unbind();
}
manager.isRecordingStart = false;
return; // Need to wait an additional frame. Kind of hacky, but oh well.
}
auto pixels = pixels_get();
renderFrames.push_back(Texture(pixels.data(), size));
if (playback.isFinished)
if (playback.time > manager.recordingEnd || playback.isFinished)
{
auto& ffmpegPath = settings.renderFFmpegPath;
auto& path = settings.renderPath;
@@ -72,7 +110,7 @@ namespace anm2ed::imgui
isSuccess = false;
break;
}
logger.info(std::format("Saved frame to PNG: {}", outputPath.string()));
logger.info(std::format("Saved frame to: {}", outputPath.string()));
}
if (isSuccess)
@@ -89,6 +127,12 @@ namespace anm2ed::imgui
}
renderFrames.clear();
pan = savedPan;
zoom = savedZoom;
settings = savedSettings;
isSizeTrySet = true;
playback.isPlaying = false;
playback.isFinished = false;
manager.isRecording = false;
@@ -127,22 +171,26 @@ namespace anm2ed::imgui
auto& shaderGrid = resources.shaders[shader::GRID];
auto& shaderTexture = resources.shaders[shader::TEXTURE];
settings.previewPan = pan;
settings.previewZoom = zoom;
auto center_view = [&]() { pan = vec2(); };
if (ImGui::Begin("Animation Preview", &settings.windowIsAnimationPreview))
{
auto childSize = ImVec2(imgui::row_widget_width_get(4),
auto childSize = ImVec2(row_widget_width_get(4),
(ImGui::GetTextLineHeightWithSpacing() * 4) + (ImGui::GetStyle().WindowPadding.y * 2));
if (ImGui::BeginChild("##Grid Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::Checkbox("Grid", &isGrid);
ImGui::SetItemTooltip("Toggle the visibility of the grid.");
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::InputInt2("Size", value_ptr(gridSize));
ImGui::InputInt2("Offset", value_ptr(gridOffset));
ImGui::SetItemTooltip("Change the grid's color.");
input_int2_range("Size", gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("Change the size of all cells in the grid.");
input_int2_range("Offset", gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("Change the offset of the grid.");
}
ImGui::EndChild();
@@ -151,19 +199,20 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##View Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::InputFloat("Zoom", &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("Change the zoom of the preview.");
auto widgetSize = imgui::widget_size_with_row_get(2);
auto widgetSize = widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutCenterView);
shortcut(settings.shortcutCenterView);
if (ImGui::Button("Center View", widgetSize)) pan = vec2();
imgui::set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
ImGui::SameLine();
imgui::shortcut(settings.shortcutFit);
shortcut(settings.shortcutFit);
if (ImGui::Button("Fit", widgetSize))
if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform));
imgui::set_item_tooltip_shortcut("Set the view to match the extent of the animation.", settings.shortcutFit);
set_item_tooltip_shortcut("Set the view to match the extent of the animation.", settings.shortcutFit);
ImGui::TextUnformatted(std::format(POSITION_FORMAT, (int)mousePos.x, (int)mousePos.y).c_str());
}
@@ -174,14 +223,19 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Background Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::ColorEdit4("Background", value_ptr(backgroundColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the background color.");
ImGui::SameLine();
ImGui::Checkbox("Axes", &isAxes);
ImGui::SetItemTooltip("Toggle the axes' visbility.");
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(axesColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Set the color of the axes.");
imgui::combo_strings("Overlay", &overlayIndex, document.animationNamesCStr);
combo_negative_one_indexed("Overlay", &overlayIndex, document.animation.labels);
ImGui::SetItemTooltip("Set an animation to be drawn over the current animation.");
ImGui::InputFloat("Alpha", &overlayTransparency, 0, 0, "%.0f");
ImGui::SetItemTooltip("Set the alpha of the overlayed animation.");
}
ImGui::EndChild();
@@ -189,12 +243,14 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Helpers Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
auto helpersChildSize = ImVec2(imgui::row_widget_width_get(2), ImGui::GetContentRegionAvail().y);
auto helpersChildSize = ImVec2(row_widget_width_get(2), ImGui::GetContentRegionAvail().y);
if (ImGui::BeginChild("##Helpers Child 1", helpersChildSize))
{
ImGui::Checkbox("Root Transform", &isRootTransform);
ImGui::SetItemTooltip("Root frames will transform the rest of the animation.");
ImGui::Checkbox("Pivots", &isPivots);
ImGui::SetItemTooltip("Toggle the visibility of the animation's pivots.");
}
ImGui::EndChild();
@@ -203,7 +259,9 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Helpers Child 2", helpersChildSize))
{
ImGui::Checkbox("Alt Icons", &isAltIcons);
ImGui::SetItemTooltip("Toggle a different appearance of the target icons.");
ImGui::Checkbox("Border", &isBorder);
ImGui::SetItemTooltip("Toggle the visibility of borders around layers.");
}
ImGui::EndChild();
}
@@ -211,7 +269,7 @@ namespace anm2ed::imgui
auto cursorScreenPos = ImGui::GetCursorScreenPos();
size_set(to_vec2(ImGui::GetContentRegionAvail()));
if (isSizeTrySet) size_set(to_vec2(ImGui::GetContentRegionAvail()));
bind();
viewport_set();
clear(backgroundColor);
@@ -234,23 +292,23 @@ namespace anm2ed::imgui
vec4 color = isOnionskin ? vec4(colorOffset, alphaOffset) : color::GREEN;
texture_render(shaderTexture, resources.icons[icon::TARGET].id, rootTransform, color);
auto icon = isAltIcons ? icon::TARGET_ALT : icon::TARGET;
texture_render(shaderTexture, resources.icons[icon].id, rootTransform, color);
}
for (auto& id : animation->layerOrder)
{
auto& layerAnimation = animation->layerAnimations.at(id);
auto& layerAnimation = animation->layerAnimations[id];
if (!layerAnimation.isVisible) continue;
auto& layer = anm2.content.layers.at(id);
if (auto frame = layerAnimation.frame_generate(time, anm2::LAYER); frame.isVisible)
if (auto frame = layerAnimation.frame_generate(time, anm2::LAYER); frame.is_visible())
{
auto spritesheet = anm2.spritesheet_get(layer.spritesheetID);
if (!spritesheet) continue;
if (!spritesheet || !spritesheet->is_valid()) continue;
auto& texture = spritesheet->texture;
if (!texture.is_valid()) continue;
auto layerModel = math::quad_model_get(frame.size, frame.position, frame.pivot,
math::percent_to_unit(frame.scale), frame.rotation);
@@ -288,7 +346,8 @@ namespace anm2ed::imgui
if (auto frame = nullAnimation.frame_generate(time, anm2::NULL_); frame.isVisible)
{
auto icon = isShowRect ? icon::POINT : icon::TARGET;
auto icon = isShowRect ? icon::POINT : isAltIcons ? icon::TARGET_ALT : icon::TARGET;
auto& size = isShowRect ? POINT_SIZE : TARGET_SIZE;
auto color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset)
: id == reference.itemID && reference.itemType == anm2::NULL_ ? color::RED
@@ -341,9 +400,8 @@ namespace anm2ed::imgui
render(animation, frameTime);
if (overlayIndex > 0)
render(document.anm2.animation_get({overlayIndex - 1}), frameTime, {},
1.0f - math::uint8_to_float(overlayTransparency));
if (auto overlayAnimation = anm2.animation_get({overlayIndex}))
render(overlayAnimation, frameTime, {}, 1.0f - math::uint8_to_float(overlayTransparency));
if (drawOrder == draw_order::ABOVE && isEnabled) onionskins_render(frameTime);
}
@@ -356,7 +414,8 @@ namespace anm2ed::imgui
if (animation && animation->triggers.isVisible && !isOnlyShowLayers)
{
if (auto trigger = animation->triggers.frame_generate(frameTime, anm2::TRIGGER); trigger.isVisible)
if (auto trigger = animation->triggers.frame_generate(frameTime, anm2::TRIGGER);
trigger.isVisible && trigger.eventID > -1)
{
auto clipMin = ImGui::GetItemRectMin();
auto clipMax = ImGui::GetItemRectMax();
@@ -374,45 +433,57 @@ namespace anm2ed::imgui
if (isPreviewHovered)
{
ImGui::SetKeyboardFocusHere(-1);
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
auto isMouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
auto isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
auto isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
auto isMouseRightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right);
auto mouseDelta = to_ivec2(ImGui::GetIO().MouseDelta);
auto mouseWheel = ImGui::GetIO().MouseWheel;
auto isLeftJustPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
auto isRightJustPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
auto isUpJustPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
auto isDownJustPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow);
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow);
auto isLeftDown = ImGui::IsKeyDown(ImGuiKey_LeftArrow);
auto isRightDown = ImGui::IsKeyDown(ImGuiKey_RightArrow);
auto isUpDown = ImGui::IsKeyDown(ImGuiKey_UpArrow);
auto isDownDown = ImGui::IsKeyDown(ImGuiKey_DownArrow);
auto isLeftReleased = ImGui::IsKeyReleased(ImGuiKey_LeftArrow);
auto isRightReleased = ImGui::IsKeyReleased(ImGuiKey_RightArrow);
auto isUpReleased = ImGui::IsKeyReleased(ImGuiKey_UpArrow);
auto isDownReleased = ImGui::IsKeyReleased(ImGuiKey_DownArrow);
auto isLeft = imgui::chord_repeating(ImGuiKey_LeftArrow);
auto isRight = imgui::chord_repeating(ImGuiKey_RightArrow);
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
auto isMouseRightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right);
auto mouseDelta = to_ivec2(ImGui::GetIO().MouseDelta);
auto mouseWheel = ImGui::GetIO().MouseWheel;
auto isZoomIn = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomIn));
auto isZoomOut = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomOut));
auto isKeyJustPressed = isLeftJustPressed || isRightJustPressed || isUpJustPressed || isDownJustPressed;
auto isKeyDown = isLeftDown || isRightDown || isUpDown || isDownDown;
auto isKeyReleased = isLeftReleased || isRightReleased || isUpReleased || isDownReleased;
auto isZoomIn = chord_repeating(string_to_chord(settings.shortcutZoomIn));
auto isZoomOut = chord_repeating(string_to_chord(settings.shortcutZoomOut));
auto isBegin = isMouseClicked || isKeyJustPressed;
auto isDuring = isMouseDown || isKeyDown;
auto isEnd = isMouseReleased || isKeyReleased;
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
auto frame = document.frame_get();
auto useTool = tool;
auto step = isMod ? canvas::STEP_FAST : canvas::STEP;
auto isKeyPressed = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
auto isKeyReleased = isLeftReleased || isRightReleased || isUpReleased || isDownReleased;
auto isBegin = isMouseClick || isKeyPressed;
auto isEnd = isMouseReleased || isKeyReleased;
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
if (isMouseMiddleDown) useTool = tool::PAN;
if (tool == tool::MOVE && isMouseRightDown) useTool = tool::SCALE;
if (tool == tool::SCALE && isMouseRightDown) useTool = tool::MOVE;
ImGui::SetMouseCursor(tool::INFO[useTool].cursor);
auto& areaType = tool::INFO[useTool].areaType;
auto cursor = areaType == tool::ANIMATION_PREVIEW || areaType == tool::ALL ? tool::INFO[useTool].cursor
: ImGuiMouseCursor_NotAllowed;
ImGui::SetMouseCursor(cursor);
ImGui::SetKeyboardFocusHere(-1);
switch (useTool)
{
@@ -423,28 +494,62 @@ namespace anm2ed::imgui
if (!frame) break;
if (isBegin) document.snapshot("Frame Position");
if (isMouseDown) frame->position = mousePos;
if (isLeft) frame->position.x -= step;
if (isRight) frame->position.x += step;
if (isUp) frame->position.y -= step;
if (isDown) frame->position.y += step;
if (isLeftPressed) frame->position.x -= step;
if (isRightPressed) frame->position.x += step;
if (isUpPressed) frame->position.y -= step;
if (isDownPressed) frame->position.y += step;
if (isEnd) document.change(Document::FRAMES);
if (isDuring)
{
if (ImGui::BeginTooltip())
{
auto positionFormat = math::vec2_format_get(frame->position);
auto positionString = std::format("Position: ({}, {})", positionFormat, positionFormat);
ImGui::Text(positionString.c_str(), frame->position.x, frame->position.y);
ImGui::EndTooltip();
}
}
break;
case tool::SCALE:
if (!frame) break;
if (isBegin) document.snapshot("Frame Scale");
if (isMouseDown) frame->scale += mouseDelta;
if (isLeft) frame->scale.x -= step;
if (isRight) frame->scale.x += step;
if (isUp) frame->scale.y -= step;
if (isDown) frame->scale.y += step;
if (isLeftPressed) frame->scale.x -= step;
if (isRightPressed) frame->scale.x += step;
if (isUpPressed) frame->scale.y -= step;
if (isDownPressed) frame->scale.y += step;
if (isDuring)
{
if (ImGui::BeginTooltip())
{
auto scaleFormat = math::vec2_format_get(frame->scale);
auto scaleString = std::format("Scale: ({}, {})", scaleFormat, scaleFormat);
ImGui::Text(scaleString.c_str(), frame->scale.x, frame->scale.y);
ImGui::EndTooltip();
}
}
if (isEnd) document.change(Document::FRAMES);
break;
case tool::ROTATE:
if (!frame) break;
if (isBegin) document.snapshot("Frame Rotation");
if (isMouseDown) frame->rotation += mouseDelta.y;
if (isLeft || isDown) frame->rotation -= step;
if (isUp || isRight) frame->rotation += step;
if (isLeftPressed || isDownPressed) frame->rotation -= step;
if (isUpPressed || isRightPressed) frame->rotation += step;
if (isDuring)
{
if (ImGui::BeginTooltip())
{
auto rotationFormat = math::float_format_get(frame->rotation);
auto rotationString = std::format("Rotation: {}", rotationFormat);
ImGui::Text(rotationString.c_str(), frame->rotation);
ImGui::EndTooltip();
}
}
if (isEnd) document.change(Document::FRAMES);
break;
default:
@@ -452,7 +557,8 @@ namespace anm2ed::imgui
}
if (mouseWheel != 0 || isZoomIn || isZoomOut)
zoom_set(zoom, pan, vec2(mousePos), (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
zoom_set(zoom, pan, mouseWheel != 0 ? vec2(mousePos) : vec2(),
(mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
}
}
ImGui::End();
@@ -478,5 +584,14 @@ namespace anm2ed::imgui
ImGui::EndPopup();
}
if (!document.isAnimationPreviewSet)
{
center_view();
zoom = settings.previewStartZoom;
document.isAnimationPreviewSet = true;
}
settings.previewStartZoom = zoom;
}
}

View File

@@ -10,6 +10,10 @@ namespace anm2ed::imgui
class AnimationPreview : public Canvas
{
bool isPreviewHovered{};
bool isSizeTrySet{true};
Settings savedSettings{};
float savedZoom{};
glm::vec2 savedPan{};
glm::ivec2 mousePos{};
std::vector<resource::Texture> renderFrames{};

View File

@@ -2,6 +2,10 @@
#include <ranges>
#include "toast.h"
#include "vector_.h"
using namespace anm2ed::util;
using namespace anm2ed::resource;
using namespace anm2ed::types;
@@ -12,10 +16,11 @@ namespace anm2ed::imgui
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto& hovered = document.hoveredAnimation;
auto& multiSelect = document.animationMultiSelect;
auto& mergeMultiSelect = document.animationMergeMultiSelect;
auto& mergeTarget = document.mergeTarget;
auto& hovered = document.animation.hovered;
auto& selection = document.animation.selection;
auto& mergeSelection = document.merge.selection;
auto& mergeReference = document.merge.reference;
auto& overlayIndex = document.overlayIndex;
hovered = -1;
@@ -25,7 +30,7 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
{
multiSelect.start(anm2.animations.items.size());
selection.start(anm2.animations.items.size());
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
{
@@ -40,11 +45,11 @@ namespace anm2ed::imgui
: font::REGULAR;
ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
ImGui::SetNextItemSelectionUserData(i);
ImGui::SetNextItemSelectionUserData((int)i);
if (selectable_input_text(animation.name, std::format("###Document #{} Animation #{}", manager.selected, i),
animation.name, multiSelect.contains(i)))
document.animation_set(i);
if (ImGui::IsItemHovered()) hovered = i;
animation.name, selection.contains((int)i)))
reference = {(int)i};
if (ImGui::IsItemHovered()) hovered = (int)i;
ImGui::PopFont();
if (ImGui::BeginItemTooltip())
@@ -69,11 +74,12 @@ namespace anm2ed::imgui
if (ImGui::BeginDragDropSource())
{
static std::vector<int> selection;
selection.assign(multiSelect.begin(), multiSelect.end());
ImGui::SetDragDropPayload("Animation Drag Drop", selection.data(), selection.size() * sizeof(int));
for (auto& i : selection)
ImGui::TextUnformatted(anm2.animations.items[i].name.c_str());
static std::vector<int> dragDropSelection{};
dragDropSelection.assign(selection.begin(), selection.end());
ImGui::SetDragDropPayload("Animation Drag Drop", dragDropSelection.data(),
dragDropSelection.size() * sizeof(int));
for (auto& i : dragDropSelection)
ImGui::Text("%s", anm2.animations.items[(int)i].name.c_str());
ImGui::EndDragDropSource();
}
@@ -85,7 +91,8 @@ namespace anm2ed::imgui
auto payloadCount = payload->DataSize / sizeof(int);
std::vector<int> indices(payloadIndices, payloadIndices + payloadCount);
std::sort(indices.begin(), indices.end());
document.animations_move(indices, i);
DOCUMENT_EDIT(document, "Move Animation(s)", Document::ANIMATIONS,
selection = vector::move_indices(anm2.animations.items, indices, i));
}
ImGui::EndDragDropTarget();
}
@@ -93,14 +100,14 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& i : multiSelect)
for (auto& i : selection)
clipboardText += anm2.animations.items[i].to_string();
clipboard.set(clipboardText);
}
@@ -111,13 +118,41 @@ namespace anm2ed::imgui
auto cut = [&]()
{
copy();
document.animations_remove();
auto remove = [&]()
{
if (!selection.empty())
{
for (auto& i : selection | std::views::reverse)
anm2.animations.items.erase(anm2.animations.items.begin() + i);
selection.clear();
}
else if (hovered > -1)
{
anm2.animations.items.erase(anm2.animations.items.begin() + hovered);
hovered = -1;
}
};
DOCUMENT_EDIT(document, "Cut Animation(s)", Document::ANIMATIONS, remove());
};
auto paste = [&]()
{
auto clipboardText = clipboard.get();
document.animations_deserialize(clipboardText);
auto deserialize = [&]()
{
auto start = selection.empty() ? anm2.animations.items.size() : *selection.rbegin() + 1;
std::set<int> indices{};
std::string errorString{};
if (anm2.animations_deserialize(clipboardText, start, indices, &errorString))
selection = indices;
else
toasts.error(std::format("Failed to deserialize animation(s): {}", errorString));
};
DOCUMENT_EDIT(document, "Paste Animation(s)", Document::ANIMATIONS, deserialize());
};
if (shortcut(settings.shortcutCut, shortcut::FOCUSED)) cut();
@@ -126,15 +161,9 @@ namespace anm2ed::imgui
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();
if (ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, !selection.empty() || hovered > -1)) cut();
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
if (ImGui::MenuItem("Paste", settings.shortcutPaste.c_str(), false, !clipboard.is_empty())) paste();
ImGui::EndPopup();
}
}
@@ -143,33 +172,98 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(5);
shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize)) document.animation_add();
if (ImGui::Button("Add", widgetSize))
{
auto 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 = 0;
if (!anm2.animations.items.empty())
index = selection.empty() ? (int)anm2.animations.items.size() - 1 : *selection.rbegin() + 1;
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
selection = {index};
reference = {index};
};
DOCUMENT_EDIT(document, "Add Animation", Document::ANIMATIONS, add());
}
set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd);
ImGui::SameLine();
ImGui::BeginDisabled(multiSelect.empty());
ImGui::BeginDisabled(selection.empty());
{
shortcut(settings.shortcutDuplicate);
if (ImGui::Button("Duplicate", widgetSize)) document.animation_duplicate();
if (ImGui::Button("Duplicate", widgetSize))
{
auto duplicate = [&]()
{
auto duplicated = selection;
auto end = std::ranges::max(duplicated);
for (auto& id : duplicated)
{
anm2.animations.items.insert(anm2.animations.items.begin() + end, anm2.animations.items[id]);
selection.insert(++end);
selection.erase(id);
}
};
DOCUMENT_EDIT(document, "Duplicate Animation(s)", Document::ANIMATIONS, duplicate());
}
set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate);
ImGui::SameLine();
if (shortcut(settings.shortcutMerge, shortcut::FOCUSED))
if (multiSelect.size() > 0) document.animations_merge_quick();
if (shortcut(settings.shortcutMerge, shortcut::FOCUSED) && !selection.empty())
{
auto merge_quick = [&]()
{
int merged{};
if (selection.contains(overlayIndex)) overlayIndex = -1;
ImGui::BeginDisabled(multiSelect.size() != 1);
if (selection.size() > 1)
merged = anm2.animations_merge(*selection.begin(), selection);
else if (selection.size() == 1 && *selection.begin() != (int)anm2.animations.items.size() - 1)
{
auto start = *selection.begin();
auto next = *selection.begin() + 1;
std::set<int> animationSet{};
animationSet.insert(start);
animationSet.insert(next);
merged = anm2.animations_merge(start, animationSet);
}
else
return;
selection = {merged};
reference = {merged};
};
DOCUMENT_EDIT(document, "Merge Animations", Document::ANIMATIONS, merge_quick())
}
ImGui::BeginDisabled(selection.size() != 1);
{
if (ImGui::Button("Merge", widgetSize))
{
mergePopup.open();
mergeMultiSelect.clear();
mergeTarget = *multiSelect.begin();
mergeSelection.clear();
mergeReference = *selection.begin();
}
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Open the merge popup.\nUsing the shortcut will merge the animations with\nthe last "
"configured merge settings.",
settings.shortcutMerge);
@@ -177,14 +271,31 @@ namespace anm2ed::imgui
ImGui::SameLine();
shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove", widgetSize)) document.animations_remove();
if (ImGui::Button("Remove", widgetSize))
{
auto remove = [&]()
{
for (auto& i : selection | std::views::reverse)
{
if (i == overlayIndex) overlayIndex = -1;
anm2.animations.items.erase(anm2.animations.items.begin() + i);
}
selection.clear();
};
DOCUMENT_EDIT(document, "Remove Animation(s)", Document::ANIMATIONS, remove());
}
set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutDuplicate);
ImGui::SameLine();
shortcut(settings.shortcutDefault);
ImGui::BeginDisabled(multiSelect.size() != 1);
if (ImGui::Button("Default", widgetSize)) document.animation_default();
ImGui::BeginDisabled(selection.size() != 1);
if (ImGui::Button("Default", widgetSize))
{
DOCUMENT_EDIT(document, "Default Animation", Document::ANIMATIONS,
anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name);
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Set the selected animation as the default.", settings.shortcutDefault);
}
@@ -196,7 +307,7 @@ namespace anm2ed::imgui
{
auto merge_close = [&]()
{
mergeMultiSelect.clear();
mergeSelection.clear();
mergePopup.close();
};
@@ -212,19 +323,21 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders))
{
mergeMultiSelect.start(anm2.animations.items.size());
mergeSelection.start(anm2.animations.items.size());
for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
{
if (i == mergeReference) continue;
ImGui::PushID(i);
ImGui::SetNextItemSelectionUserData(i);
ImGui::Selectable(animation.name.c_str(), mergeMultiSelect.contains(i));
ImGui::Selectable(animation.name.c_str(), mergeSelection.contains(i));
ImGui::PopID();
}
mergeMultiSelect.finish();
mergeSelection.finish();
}
ImGui::EndChild();
@@ -258,7 +371,17 @@ namespace anm2ed::imgui
if (ImGui::Button("Merge", widgetSize))
{
document.animations_merge((merge::Type)type, isDeleteAnimationsAfter);
auto merge = [&]()
{
if (mergeSelection.contains(overlayIndex)) overlayIndex = -1;
auto merged =
anm2.animations_merge(mergeReference, mergeSelection, (merge::Type)type, isDeleteAnimationsAfter);
selection = {merged};
reference = {merged};
};
DOCUMENT_EDIT(document, "Merge Animations", Document::ANIMATIONS, merge());
merge_close();
}
ImGui::SameLine();

View File

@@ -2,6 +2,10 @@
#include <ranges>
#include "map_.h"
#include "toast.h"
using namespace anm2ed::util;
using namespace anm2ed::resource;
using namespace anm2ed::types;
@@ -11,10 +15,10 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& unused = document.unusedEventIDs;
auto& hovered = document.hoveredEvent;
auto& reference = document.referenceEvent;
auto& multiSelect = document.eventMultiSelect;
auto& unused = document.event.unused;
auto& hovered = document.event.hovered;
auto& reference = document.event.reference;
auto& selection = document.event.selection;
hovered = -1;
@@ -24,23 +28,15 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Events Child", childSize, true))
{
multiSelect.start(anm2.content.events.size());
selection.start(anm2.content.events.size());
for (auto& [id, event] : anm2.content.events)
{
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
ImGui::Selectable(event.name.c_str(), multiSelect.contains(id));
if (ImGui::IsItemHovered())
{
hovered = id;
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
reference = id;
editEvent = document.anm2.content.events[reference];
propertiesPopup.open();
}
}
if (selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
event.name, selection.contains(id)))
if (ImGui::IsItemHovered()) hovered = id;
if (ImGui::BeginItemTooltip())
{
@@ -52,14 +48,14 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& id : multiSelect)
for (auto& id : selection)
clipboardText += anm2.content.events[id].to_string(id);
clipboard.set(clipboardText);
}
@@ -69,8 +65,12 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
auto clipboardText = clipboard.get();
document.events_deserialize(clipboardText, type);
std::string errorString{};
document.snapshot("Paste Event(s)");
if (anm2.events_deserialize(clipboard.get(), type, &errorString))
document.change(Document::EVENTS);
else
toasts.error(std::format("Failed to deserialize event(s): {}", errorString));
};
if (shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
@@ -78,25 +78,16 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::BeginDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
ImGui::EndDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
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", !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);
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
@@ -108,53 +99,36 @@ namespace anm2ed::imgui
shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize))
{
reference = -1;
editEvent = anm2::Event();
propertiesPopup.open();
auto add = [&]()
{
auto id = map::next_id_get(anm2.content.events);
anm2.content.events[id] = anm2::Event();
selection = {id};
reference = {id};
};
DOCUMENT_EDIT(document, "Add Event", Document::EVENTS, add());
}
set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
ImGui::SameLine();
shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unused.empty());
if (ImGui::Button("Remove Unused", widgetSize)) document.events_remove_unused();
if (ImGui::Button("Remove Unused", widgetSize))
{
auto remove_unused = [&]()
{
for (auto& id : unused)
anm2.content.events.erase(id);
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Events", Document::EVENTS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)",
settings.shortcutRemove);
}
ImGui::End();
propertiesPopup.trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto childSize = child_size_get(2);
auto& event = editEvent;
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
{
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
input_text_string("Name", &event.name);
ImGui::SetItemTooltip("Set the event's name.");
combo_strings("Sound", &event.soundID, document.soundNames);
ImGui::SetItemTooltip("Set the event sound; it will play when a trigger associated with this event activates.");
}
ImGui::EndChild();
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
{
document.event_set(event);
propertiesPopup.close();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) propertiesPopup.close();
propertiesPopup.end();
ImGui::EndPopup();
}
}
}

View File

@@ -9,9 +9,6 @@ namespace anm2ed::imgui
{
class Events
{
anm2::Event editEvent{};
PopupHelper propertiesPopup{PopupHelper("Event Properties", POPUP_SMALL_NO_HEIGHT)};
public:
void update(Manager&, Settings&, Resources&, Clipboard&);
};

View File

@@ -1,7 +1,5 @@
#include "frame_properties.h"
#include <ranges>
#include <glm/gtc/type_ptr.hpp>
#include "math_.h"
@@ -18,9 +16,7 @@ namespace anm2ed::imgui
if (ImGui::Begin("Frame Properties", &settings.windowIsFrameProperties))
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto& type = reference.itemType;
auto& type = document.reference.itemType;
auto frame = document.frame_get();
auto useFrame = frame ? *frame : anm2::Frame();
@@ -28,13 +24,16 @@ namespace anm2ed::imgui
{
if (type == anm2::TRIGGER)
{
std::vector<std::string> eventNames{};
for (auto& event : anm2.content.events | std::views::values)
eventNames.emplace_back(event.name);
if (imgui::combo_strings("Event", frame ? &useFrame.eventID : &dummy_value<int>(), eventNames))
if (combo_negative_one_indexed("Event", frame ? &useFrame.eventID : &dummy_value<int>(),
document.event.labels))
DOCUMENT_EDIT(document, "Trigger Event", Document::FRAMES, frame->eventID = useFrame.eventID);
ImGui::SetItemTooltip("Change the event this trigger uses.");
if (combo_negative_one_indexed("Sound", frame ? &useFrame.soundID : &dummy_value<int>(),
document.sound.labels))
DOCUMENT_EDIT(document, "Trigger Sound", Document::FRAMES, frame->soundID = useFrame.soundID);
ImGui::SetItemTooltip("Change the sound this trigger uses.");
if (ImGui::InputInt("At Frame", frame ? &useFrame.atFrame : &dummy_value<int>(), imgui::STEP,
imgui::STEP_FAST, !frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, "Trigger At Frame", Document::FRAMES, frame->atFrame = useFrame.atFrame);

View File

@@ -2,6 +2,10 @@
#include <ranges>
#include "map_.h"
#include "toast.h"
using namespace anm2ed::util;
using namespace anm2ed::resource;
using namespace anm2ed::types;
@@ -11,10 +15,10 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.referenceLayer;
auto& unused = document.unusedLayerIDs;
auto& hovered = document.hoveredLayer;
auto& multiSelect = document.layersMultiSelect;
auto& reference = document.layer.reference;
auto& unused = document.layer.unused;
auto& hovered = document.layer.hovered;
auto& selection = document.layer.selection;
auto& propertiesPopup = manager.layerPropertiesPopup;
hovered = -1;
@@ -25,11 +29,11 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Layers Child", childSize, true))
{
multiSelect.start(anm2.content.layers.size());
selection.start(anm2.content.layers.size());
for (auto& [id, layer] : anm2.content.layers)
{
auto isSelected = multiSelect.contains(id);
auto isSelected = selection.contains(id);
ImGui::PushID(id);
@@ -55,14 +59,14 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& id : multiSelect)
for (auto& id : selection)
clipboardText += anm2.content.layers[id].to_string(id);
clipboard.set(clipboardText);
}
@@ -72,8 +76,12 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
auto clipboardText = clipboard.get();
document.layers_deserialize(clipboardText, type);
std::string errorString{};
document.snapshot("Paste Layer(s)");
if (anm2.layers_deserialize(clipboard.get(), type, &errorString))
document.change(Document::NULLS);
else
toasts.error(std::format("Failed to deserialize layer(s): {}", errorString));
};
if (shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
@@ -81,25 +89,16 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::BeginDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
ImGui::EndDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
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", !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);
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
@@ -115,7 +114,17 @@ namespace anm2ed::imgui
shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unused.empty());
if (ImGui::Button("Remove Unused", widgetSize)) document.layers_remove_unused();
if (ImGui::Button("Remove Unused", widgetSize))
{
auto remove_unused = [&]()
{
for (auto& id : unused)
anm2.content.layers.erase(id);
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Layers", Document::LAYERS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
settings.shortcutRemove);
@@ -134,7 +143,7 @@ namespace anm2ed::imgui
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
input_text_string("Name", &layer.name);
ImGui::SetItemTooltip("Set the item's name.");
combo_strings("Spritesheet", &layer.spritesheetID, document.spritesheetNames);
combo_negative_one_indexed("Spritesheet", &layer.spritesheetID, document.spritesheet.labels);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
}
ImGui::EndChild();
@@ -143,7 +152,26 @@ namespace anm2ed::imgui
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
{
document.layer_set(layer);
auto add = [&]()
{
auto id = map::next_id_get(anm2.content.layers);
anm2.content.layers[id] = layer;
selection = {id};
};
auto set = [&]()
{
anm2.content.layers[reference] = layer;
selection = {reference};
};
if (reference == -1)
{
DOCUMENT_EDIT(document, "Add Layer", Document::LAYERS, add());
}
else
DOCUMENT_EDIT(document, "Set Layer Properties", Document::LAYERS, set());
manager.layer_properties_close();
}

View File

@@ -2,7 +2,11 @@
#include <ranges>
#include "map_.h"
#include "toast.h"
using namespace anm2ed::resource;
using namespace anm2ed::util;
using namespace anm2ed::types;
namespace anm2ed::imgui
@@ -11,10 +15,10 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.referenceNull;
auto& unused = document.unusedNullIDs;
auto& hovered = document.hoveredNull;
auto& multiSelect = document.nullMultiSelect;
auto& reference = document.null.reference;
auto& unused = document.null.unused;
auto& hovered = document.null.hovered;
auto& selection = document.null.selection;
auto& propertiesPopup = manager.nullPropertiesPopup;
hovered = -1;
@@ -25,11 +29,11 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Nulls Child", childSize, true))
{
multiSelect.start(anm2.content.nulls.size());
selection.start(anm2.content.nulls.size());
for (auto& [id, null] : anm2.content.nulls)
{
auto isSelected = multiSelect.contains(id);
auto isSelected = selection.contains(id);
auto isReferenced = reference == id;
ImGui::PushID(id);
@@ -55,14 +59,14 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& id : multiSelect)
for (auto& id : selection)
clipboardText += anm2.content.nulls[id].to_string(id);
clipboard.set(clipboardText);
}
@@ -72,8 +76,12 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
auto clipboardText = clipboard.get();
document.nulls_deserialize(clipboardText, type);
std::string errorString{};
document.snapshot("Paste Null(s)");
if (anm2.nulls_deserialize(clipboard.get(), type, &errorString))
document.change(Document::NULLS);
else
toasts.error(std::format("Failed to deserialize null(s): {}", errorString));
};
if (shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
@@ -81,25 +89,16 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::BeginDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
ImGui::EndDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, selection.empty() || hovered > -1)) copy();
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", !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);
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
@@ -115,7 +114,17 @@ namespace anm2ed::imgui
shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unused.empty());
if (ImGui::Button("Remove Unused", widgetSize)) document.nulls_remove_unused();
if (ImGui::Button("Remove Unused", widgetSize))
{
auto remove_unused = [&]()
{
for (auto& id : unused)
anm2.content.nulls.erase(id);
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Events", Document::EVENTS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)", settings.shortcutRemove);
}
@@ -143,7 +152,26 @@ namespace anm2ed::imgui
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
{
document.null_set(null);
auto add = [&]()
{
auto id = map::next_id_get(anm2.content.nulls);
anm2.content.nulls[id] = null;
selection = {id};
};
auto set = [&]()
{
anm2.content.nulls[reference] = null;
selection = {reference};
};
if (reference == -1)
{
DOCUMENT_EDIT(document, "Add Null", Document::NULLS, add());
}
else
DOCUMENT_EDIT(document, "Set Null Properties", Document::NULLS, set());
manager.null_properties_close();
}

View File

@@ -2,6 +2,8 @@
#include <ranges>
#include "toast.h"
using namespace anm2ed::dialog;
using namespace anm2ed::types;
using namespace anm2ed::resource;
@@ -12,10 +14,10 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.referenceNull;
auto& unused = document.unusedNullIDs;
auto& hovered = document.hoveredNull;
auto& multiSelect = document.soundMultiSelect;
auto& reference = document.sound.reference;
auto& unused = document.sound.unused;
auto& hovered = document.null.hovered;
auto& selection = document.sound.selection;
hovered = -1;
@@ -25,24 +27,18 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Sounds Child", childSize, true))
{
multiSelect.start(anm2.content.sounds.size());
selection.start(anm2.content.sounds.size());
for (auto& [id, sound] : anm2.content.sounds)
{
auto isSelected = multiSelect.contains(id);
auto isSelected = selection.contains(id);
auto isReferenced = reference == id;
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
if (ImGui::Selectable(std::format(anm2::SOUND_FORMAT, id, sound.path.string()).c_str(), isSelected))
sound.audio.play();
if (ImGui::IsItemHovered())
{
hovered = id;
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
;
}
if (ImGui::Selectable(sound.path.c_str(), isSelected)) sound.play();
if (ImGui::IsItemHovered()) hovered = id;
if (isReferenced) ImGui::PopFont();
@@ -58,14 +54,14 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& id : multiSelect)
for (auto& id : selection)
clipboardText += anm2.content.sounds[id].to_string(id);
clipboard.set(clipboardText);
}
@@ -75,8 +71,12 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
auto clipboardText = clipboard.get();
document.sounds_deserialize(clipboardText, type);
std::string errorString{};
document.snapshot("Paste Sound(s)");
if (anm2.sounds_deserialize(clipboard.get(), document.directory_get(), type, &errorString))
document.change(Document::SOUNDS);
else
toasts.error(std::format("Failed to deserialize sound(s): {}", errorString));
};
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
@@ -84,25 +84,16 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::BeginDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str());
ImGui::EndDisabled();
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), !selection.empty() && hovered > -1)) copy();
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", !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);
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
@@ -119,7 +110,16 @@ namespace anm2ed::imgui
imgui::shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unused.empty());
if (ImGui::Button("Remove Unused", widgetSize))
;
{
auto remove_unused = [&]()
{
for (auto& id : unused)
anm2.content.sounds.erase(id);
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Sounds", Document::SOUNDS, remove_unused());
};
ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused sounds (i.e., ones not used in any trigger.)",
settings.shortcutRemove);
@@ -128,7 +128,20 @@ namespace anm2ed::imgui
if (dialog.is_selected(dialog::SOUND_OPEN))
{
document.sound_add(dialog.path);
auto add = [&]()
{
int id{};
if (anm2.sound_add(document.directory_get(), dialog.path, id))
{
selection = {id};
toasts.info(std::format("Initialized sound #{}: {}", id, dialog.path));
}
else
toasts.error(std::format("Failed to initialize sound: {}", dialog.path));
};
DOCUMENT_EDIT(document, "Add Sound", Document::SOUNDS, add());
dialog.reset();
}
}

View File

@@ -1,5 +1,8 @@
#include "spritesheet_editor.h"
#include <cmath>
#include <utility>
#include "math_.h"
#include "tool.h"
#include "types.h"
@@ -20,17 +23,20 @@ namespace anm2ed::imgui
void SpritesheetEditor::update(Manager& manager, Settings& settings, Resources& resources)
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto& referenceSpritesheet = document.referenceSpritesheet;
auto& referenceSpritesheet = document.spritesheet.reference;
auto& pan = document.editorPan;
auto& zoom = document.editorZoom;
auto& backgroundColor = settings.editorBackgroundColor;
auto& gridColor = settings.editorGridColor;
auto& gridSize = settings.editorGridSize;
auto& gridOffset = settings.editorGridOffset;
auto& toolColor = settings.toolColor;
auto& isGrid = settings.editorIsGrid;
auto& isGridSnap = settings.editorIsGridSnap;
auto& zoomStep = settings.viewZoomStep;
auto& isBorder = settings.editorIsBorder;
auto spritesheet = document.spritesheet_get();
@@ -39,6 +45,8 @@ namespace anm2ed::imgui
auto& shaderTexture = resources.shaders[shader::TEXTURE];
auto& dashedShader = resources.shaders[shader::DASHED];
auto center_view = [&]() { pan = -size * 0.5f; };
if (ImGui::Begin("Spritesheet Editor", &settings.windowIsSpritesheetEditor))
{
auto childSize = ImVec2(imgui::row_widget_width_get(3),
@@ -47,10 +55,19 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Grid Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::Checkbox("Grid", &isGrid);
ImGui::SetItemTooltip("Toggle the visibility of the grid.");
ImGui::SameLine();
ImGui::Checkbox("Snap", &isGridSnap);
ImGui::SetItemTooltip("Cropping will snap points to the grid.");
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::InputInt2("Size", value_ptr(gridSize));
ImGui::InputInt2("Offset", value_ptr(gridOffset));
ImGui::SetItemTooltip("Change the grid's color.");
input_int2_range("Size", gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("Change the size of all cells in the grid.");
input_int2_range("Offset", gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("Change the offset of the grid.");
}
ImGui::EndChild();
@@ -59,11 +76,12 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##View Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::InputFloat("Zoom", &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("Change the zoom of the editor.");
auto widgetSize = ImVec2(imgui::row_widget_width_get(2), 0);
imgui::shortcut(settings.shortcutCenterView);
if (ImGui::Button("Center View", widgetSize)) pan = -size * 0.5f;
if (ImGui::Button("Center View", widgetSize)) center_view();
imgui::set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
ImGui::SameLine();
@@ -82,8 +100,10 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Background Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::ColorEdit4("Background", value_ptr(backgroundColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the background color.");
ImGui::Checkbox("Border", &isBorder);
ImGui::SetItemTooltip("Toggle a border appearing around the spritesheet.");
}
ImGui::EndChild();
@@ -127,45 +147,85 @@ namespace anm2ed::imgui
if (ImGui::IsItemHovered())
{
ImGui::SetKeyboardFocusHere(-1);
previousMousePos = mousePos;
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
auto isMouseClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
auto isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
auto isMouseLeftClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
auto isMouseLeftReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
auto isMouseLeftDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
auto isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
auto isMouseRightClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
auto isMouseRightDown = ImGui::IsMouseDown(ImGuiMouseButton_Right);
auto isMouseRightReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
auto mouseDelta = to_ivec2(ImGui::GetIO().MouseDelta);
auto mouseWheel = ImGui::GetIO().MouseWheel;
auto& toolColor = settings.toolColor;
auto isZoomIn = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomIn));
auto isZoomOut = imgui::chord_repeating(imgui::string_to_chord(settings.shortcutZoomOut));
auto isLeft = imgui::chord_repeating(ImGuiKey_LeftArrow);
auto isRight = imgui::chord_repeating(ImGuiKey_RightArrow);
auto isUp = imgui::chord_repeating(ImGuiKey_UpArrow);
auto isDown = imgui::chord_repeating(ImGuiKey_DownArrow);
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
auto step = isMod ? canvas::STEP_FAST : canvas::STEP;
auto useTool = tool;
auto isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
auto isMouseReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left);
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
auto isMouseClicked = isMouseLeftClicked || isMouseRightClicked;
auto isMouseDown = isMouseLeftDown || isMouseRightDown;
auto isMouseReleased = isMouseLeftReleased || isMouseRightReleased;
auto isLeftJustPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false);
auto isRightJustPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow, false);
auto isUpJustPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow, false);
auto isDownJustPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow, false);
auto isLeftPressed = ImGui::IsKeyPressed(ImGuiKey_LeftArrow);
auto isRightPressed = ImGui::IsKeyPressed(ImGuiKey_RightArrow);
auto isUpPressed = ImGui::IsKeyPressed(ImGuiKey_UpArrow);
auto isDownPressed = ImGui::IsKeyPressed(ImGuiKey_DownArrow);
auto isLeftDown = ImGui::IsKeyDown(ImGuiKey_LeftArrow);
auto isRightDown = ImGui::IsKeyDown(ImGuiKey_RightArrow);
auto isUpDown = ImGui::IsKeyDown(ImGuiKey_UpArrow);
auto isDownDown = ImGui::IsKeyDown(ImGuiKey_DownArrow);
auto isLeftReleased = ImGui::IsKeyReleased(ImGuiKey_LeftArrow);
auto isRightReleased = ImGui::IsKeyReleased(ImGuiKey_RightArrow);
auto isUpReleased = ImGui::IsKeyReleased(ImGuiKey_UpArrow);
auto isDownReleased = ImGui::IsKeyReleased(ImGuiKey_DownArrow);
auto frame = document.frame_get();
auto isKeyPressed = isLeftPressed || isRightPressed || isUpPressed || isDownPressed;
auto isKeyJustPressed = isLeftJustPressed || isRightJustPressed || isUpJustPressed || isDownJustPressed;
auto isKeyDown = isLeftDown || isRightDown || isUpDown || isDownDown;
auto isKeyReleased = isLeftReleased || isRightReleased || isUpReleased || isDownReleased;
auto isBegin = isMouseClick || isKeyPressed;
auto isZoomIn = chord_repeating(string_to_chord(settings.shortcutZoomIn));
auto isZoomOut = chord_repeating(string_to_chord(settings.shortcutZoomOut));
auto isBegin = isMouseClicked || isKeyJustPressed;
auto isDuring = isMouseDown || isKeyDown;
auto isEnd = isMouseReleased || isKeyReleased;
if (isMouseMiddleDown) useTool = tool::PAN;
auto isMod = ImGui::IsKeyDown(ImGuiMod_Shift);
ImGui::SetMouseCursor(tool::INFO[useTool].cursor);
auto frame = document.frame_get();
auto useTool = tool;
auto step = isMod ? canvas::STEP_FAST : canvas::STEP;
auto stepX = isGridSnap ? step * gridSize.x : step;
auto stepY = isGridSnap ? step * gridSize.y : step;
previousMousePos = mousePos;
mousePos = position_translate(zoom, pan, to_vec2(ImGui::GetMousePos()) - to_vec2(cursorScreenPos));
auto snap_rect = [&](glm::vec2 minPoint, glm::vec2 maxPoint)
{
if (isGridSnap)
{
if (gridSize.x != 0)
{
minPoint.x = std::floor(minPoint.x / gridSize.x) * gridSize.x;
maxPoint.x = std::ceil(maxPoint.x / gridSize.x) * gridSize.x;
}
if (gridSize.y != 0)
{
minPoint.y = std::floor(minPoint.y / gridSize.y) * gridSize.y;
maxPoint.y = std::ceil(maxPoint.y / gridSize.y) * gridSize.y;
}
}
return std::pair{minPoint, maxPoint};
};
if (isMouseMiddleDown) useTool = tool::PAN;
if (tool == tool::MOVE && isMouseRightDown) useTool = tool::CROP;
if (tool == tool::CROP && isMouseRightDown) useTool = tool::MOVE;
if (tool == tool::DRAW && isMouseRightDown) useTool = tool::ERASE;
if (tool == tool::ERASE && isMouseRightDown) useTool = tool::DRAW;
auto& areaType = tool::INFO[useTool].areaType;
auto cursor = areaType == tool::SPRITESHEET_EDITOR || areaType == tool::ALL ? tool::INFO[useTool].cursor
: ImGuiMouseCursor_NotAllowed;
ImGui::SetMouseCursor(cursor);
ImGui::SetKeyboardFocusHere(-1);
switch (useTool)
{
@@ -176,36 +236,84 @@ namespace anm2ed::imgui
if (!frame) break;
if (isBegin) document.snapshot("Frame Pivot");
if (isMouseDown) frame->pivot = ivec2(mousePos - frame->crop);
if (isLeft) frame->pivot.x -= step;
if (isRight) frame->pivot.x += step;
if (isUp) frame->pivot.y -= step;
if (isDown) frame->pivot.y += step;
if (isLeftPressed) frame->pivot.x -= step;
if (isRightPressed) frame->pivot.x += step;
if (isUpPressed) frame->pivot.y -= step;
if (isDownPressed) frame->pivot.y += step;
if (isDuring)
{
if (ImGui::BeginTooltip())
{
auto pivotFormat = math::vec2_format_get(frame->pivot);
auto pivotString = std::format("Pivot: ({}, {})", pivotFormat, pivotFormat);
ImGui::Text(pivotString.c_str(), frame->pivot.x, frame->pivot.y);
ImGui::EndTooltip();
}
}
if (isEnd) document.change(Document::FRAMES);
break;
case tool::CROP:
if (!frame) break;
if (isBegin) document.snapshot(isMod ? "Frame Size" : "Frame Crop");
if (isMouseClicked) frame->crop = ivec2(mousePos);
if (isMouseDown) frame->size = ivec2(mousePos - frame->crop);
if (isLeft) isMod ? frame->size.x -= step : frame->crop.x -= step;
if (isRight) isMod ? frame->size.x += step : frame->crop.x += step;
if (isUp) isMod ? frame->size.y -= step : frame->crop.y -= step;
if (isDown) isMod ? frame->size.y += step : frame->crop.y += step;
if (isBegin) document.snapshot("Frame Crop");
if (isMouseClicked)
{
cropAnchor = mousePos;
frame->crop = cropAnchor;
frame->size = vec2();
}
if (isMouseDown)
{
auto [minPoint, maxPoint] = snap_rect(glm::min(cropAnchor, mousePos), glm::max(cropAnchor, mousePos));
frame->crop = minPoint;
frame->size = maxPoint - minPoint;
}
if (isLeftPressed) frame->crop.x -= stepX;
if (isRightPressed) frame->crop.x += stepX;
if (isUpPressed) frame->crop.y -= stepY;
if (isDownPressed) frame->crop.y += stepY;
if (isDuring)
{
if (!isMouseDown)
{
auto minPoint = glm::min(frame->crop, frame->crop + frame->size);
auto maxPoint = glm::max(frame->crop, frame->crop + frame->size);
frame->crop = minPoint;
frame->size = maxPoint - minPoint;
if (isGridSnap)
{
auto [snapMin, snapMax] = snap_rect(frame->crop, frame->crop + frame->size);
frame->crop = snapMin;
frame->size = snapMax - snapMin;
}
}
if (ImGui::BeginTooltip())
{
auto cropFormat = math::vec2_format_get(frame->crop);
auto sizeFormat = math::vec2_format_get(frame->size);
auto cropString = std::format("Crop: ({}, {})", cropFormat, cropFormat);
auto sizeString = std::format("Size: ({}, {})", sizeFormat, sizeFormat);
ImGui::Text(cropString.c_str(), frame->crop.x, frame->crop.y);
ImGui::Text(sizeString.c_str(), frame->size.x, frame->size.y);
ImGui::EndTooltip();
}
}
if (isEnd) document.change(Document::FRAMES);
break;
case tool::DRAW:
case tool::ERASE:
{
if (!spritesheet) break;
auto color = tool == tool::DRAW ? toolColor : vec4();
if (isMouseClicked) document.snapshot(tool == tool::DRAW ? "Draw" : "Erase");
auto color = useTool == tool::DRAW ? toolColor : vec4();
if (isMouseClicked) document.snapshot(useTool == tool::DRAW ? "Draw" : "Erase");
if (isMouseDown) spritesheet->texture.pixel_line(ivec2(previousMousePos), ivec2(mousePos), color);
if (isMouseReleased) document.change(Document::FRAMES);
if (isMouseReleased) document.change(Document::SPRITESHEETS);
break;
}
case tool::COLOR_PICKER:
{
if (isMouseDown)
if (isDuring)
{
auto position = to_vec2(ImGui::GetMousePos());
toolColor = pixel_read(position, {settings.windowSize.x, settings.windowSize.y});
@@ -222,9 +330,26 @@ namespace anm2ed::imgui
}
if (mouseWheel != 0 || isZoomIn || isZoomOut)
zoom_set(zoom, pan, mousePos, (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
{
auto focus = mouseWheel != 0 ? vec2(mousePos) : vec2();
if (auto spritesheet = document.spritesheet_get(); spritesheet && mouseWheel == 0)
focus = spritesheet->texture.size / 2;
zoom_set(zoom, pan, focus, (mouseWheel > 0 || isZoomIn) ? zoomStep : -zoomStep);
}
}
}
ImGui::End();
if (!document.isSpritesheetEditorSet)
{
size = settings.editorSize;
zoom = settings.editorStartZoom;
center_view();
document.isSpritesheetEditorSet = true;
}
settings.editorSize = size;
settings.editorStartZoom = zoom;
}
}

View File

@@ -11,6 +11,7 @@ namespace anm2ed::imgui
{
glm::vec2 mousePos{};
glm::vec2 previousMousePos{};
glm::vec2 cropAnchor{};
public:
SpritesheetEditor();

View File

@@ -2,6 +2,7 @@
#include <ranges>
#include "document.h"
#include "toast.h"
using namespace anm2ed::types;
@@ -15,10 +16,10 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& multiSelect = document.spritesheetMultiSelect;
auto& unused = document.unusedSpritesheetIDs;
auto& hovered = document.hoveredSpritesheet;
auto& reference = document.referenceSpritesheet;
auto& selection = document.spritesheet.selection;
auto& unused = document.spritesheet.unused;
auto& hovered = document.spritesheet.hovered;
auto& reference = document.spritesheet.reference;
hovered = -1;
@@ -30,10 +31,10 @@ namespace anm2ed::imgui
{
auto copy = [&]()
{
if (!multiSelect.empty())
if (!selection.empty())
{
std::string clipboardText{};
for (auto& id : multiSelect)
for (auto& id : selection)
clipboardText += anm2.content.spritesheets[id].to_string(id);
clipboard.set(clipboardText);
}
@@ -43,43 +44,38 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
auto clipboardText = clipboard.get();
document.spritesheets_deserialize(clipboardText, type);
std::string errorString{};
document.snapshot("Paste Spritesheet(s)");
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get(), type, &errorString))
document.change(Document::SPRITESHEETS);
else
toasts.error(std::format("Failed to deserialize spritesheet(s): {}", errorString));
};
if (imgui::shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
if (imgui::shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste(merge::APPEND);
if (shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
if (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::MenuItem("Cut", settings.shortcutCut.c_str(), false, true);
ImGui::BeginDisabled(multiSelect.empty() && hovered == -1);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str())) copy();
ImGui::EndDisabled();
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
ImGui::BeginDisabled(clipboard.is_empty());
if (ImGui::BeginMenu("Paste", !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);
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
ImGui::EndMenu();
}
ImGui::EndMenu();
}
ImGui::EndDisabled();
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
};
auto childSize = imgui::size_without_footer_get(2);
auto childSize = size_without_footer_get(2);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
@@ -89,7 +85,7 @@ namespace anm2ed::imgui
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
multiSelect.start(anm2.content.spritesheets.size());
selection.start(anm2.content.spritesheets.size());
for (auto& [id, spritesheet] : anm2.content.spritesheets)
{
@@ -97,7 +93,7 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Spritesheet Child", spritesheetChildSize, ImGuiChildFlags_Borders))
{
auto isSelected = multiSelect.contains(id);
auto isSelected = selection.contains(id);
auto isReferenced = id == reference;
auto cursorPos = ImGui::GetCursorPos();
auto& texture = spritesheet.texture.is_valid() ? spritesheet.texture : resources.icons[icon::NONE];
@@ -177,7 +173,7 @@ namespace anm2ed::imgui
ImGui::PopID();
}
multiSelect.finish();
selection.finish();
ImGui::PopStyleVar(2);
@@ -185,11 +181,11 @@ namespace anm2ed::imgui
}
ImGui::EndChild();
auto rowOneWidgetSize = imgui::widget_size_with_row_get(4);
auto rowOneWidgetSize = widget_size_with_row_get(4);
imgui::shortcut(settings.shortcutAdd);
shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_OPEN);
imgui::set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd);
set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd);
if (dialog.is_selected(dialog::SPRITESHEET_OPEN))
{
@@ -199,16 +195,21 @@ namespace anm2ed::imgui
ImGui::SameLine();
ImGui::BeginDisabled(multiSelect.empty());
ImGui::BeginDisabled(selection.empty());
{
if (ImGui::Button("Reload", rowOneWidgetSize))
{
for (auto& id : multiSelect)
auto reload = [&]()
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet.reload(document.directory_get());
toasts.info(std::format("Reloaded spritesheet #{}: {}", id, spritesheet.path.string()));
}
for (auto& id : selection)
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet.reload(document.directory_get());
toasts.info(std::format("Reloaded spritesheet #{}: {}", id, spritesheet.path.string()));
}
};
DOCUMENT_EDIT(document, "Reload Spritesheet(s)", Document::SPRITESHEETS, reload());
}
ImGui::SetItemTooltip("Reloads the selected spritesheets.");
}
@@ -216,7 +217,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
ImGui::BeginDisabled(multiSelect.size() != 1);
ImGui::BeginDisabled(selection.size() != 1);
{
if (ImGui::Button("Replace", rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_REPLACE);
ImGui::SetItemTooltip("Replace the selected spritesheet with a new one.");
@@ -225,10 +226,15 @@ namespace anm2ed::imgui
if (dialog.is_selected(dialog::SPRITESHEET_REPLACE))
{
auto& id = *multiSelect.begin();
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet = anm2::Spritesheet(document.directory_get(), dialog.path);
toasts.info(std::format("Replaced spritesheet #{}: {}", id, spritesheet.path.string()));
auto replace = [&]()
{
auto& id = *selection.begin();
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet = anm2::Spritesheet(document.directory_get(), dialog.path);
toasts.info(std::format("Replaced spritesheet #{}: {}", id, spritesheet.path.string()));
};
DOCUMENT_EDIT(document, "Replace Spritesheet", Document::SPRITESHEETS, replace());
dialog.reset();
}
@@ -236,50 +242,54 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(unused.empty());
{
imgui::shortcut(settings.shortcutRemove);
shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove Unused", rowOneWidgetSize))
{
for (auto& id : unused)
auto remove_unused = [&]()
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
toasts.info(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
anm2.spritesheet_remove(id);
}
unused.clear();
document.change(Document::SPRITESHEETS);
for (auto& id : unused)
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
toasts.info(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
anm2.content.spritesheets.erase(id);
}
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Spritesheets", Document::SPRITESHEETS, remove_unused());
}
imgui::set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
settings.shortcutRemove);
set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
settings.shortcutRemove);
}
ImGui::EndDisabled();
auto rowTwoWidgetSize = imgui::widget_size_with_row_get(3);
auto rowTwoWidgetSize = widget_size_with_row_get(3);
imgui::shortcut(settings.shortcutSelectAll);
ImGui::BeginDisabled(multiSelect.size() == anm2.content.spritesheets.size());
shortcut(settings.shortcutSelectAll);
ImGui::BeginDisabled(selection.size() == anm2.content.spritesheets.size());
{
if (ImGui::Button("Select All", rowTwoWidgetSize))
for (auto& id : anm2.content.spritesheets | std::views::keys)
multiSelect.insert(id);
selection.insert(id);
}
ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Select all spritesheets.", settings.shortcutSelectAll);
set_item_tooltip_shortcut("Select all spritesheets.", settings.shortcutSelectAll);
ImGui::SameLine();
imgui::shortcut(settings.shortcutSelectNone);
ImGui::BeginDisabled(multiSelect.empty());
if (ImGui::Button("Select None", rowTwoWidgetSize)) multiSelect.clear();
imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
shortcut(settings.shortcutSelectNone);
ImGui::BeginDisabled(selection.empty());
if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(multiSelect.empty());
ImGui::BeginDisabled(selection.empty());
{
if (ImGui::Button("Save", rowTwoWidgetSize))
{
for (auto& id : multiSelect)
for (auto& id : selection)
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
if (spritesheet.save(document.directory_get()))

View File

@@ -4,6 +4,8 @@
#include <imgui_internal.h>
#include "toast.h"
using namespace anm2ed::resource;
using namespace anm2ed::types;
using namespace glm;
@@ -48,41 +50,59 @@ namespace anm2ed::imgui
void Timeline::context_menu(Document& document, Settings& settings, Clipboard& clipboard)
{
auto& hoveredFrame = document.hoveredFrame;
auto& anm2 = document.anm2;
auto& reference = document.reference;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
auto copy = [&]()
{
if (auto frame = document.anm2.frame_get(hoveredFrame))
{
clipboard.set(frame->to_string(hoveredFrame.itemType));
}
if (auto frame = anm2.frame_get(hoveredFrame)) clipboard.set(frame->to_string(hoveredFrame.itemType));
};
auto cut = [&]()
{
copy();
document.frames_delete(document.item_get());
auto frames_delete = [&]()
{
if (auto item = anm2.item_get(reference); item)
{
item->frames.erase(item->frames.begin() + reference.frameIndex);
reference.frameIndex = glm::max(-1, --reference.frameIndex);
}
};
DOCUMENT_EDIT(document, "Cut Frame(s)", Document::FRAMES, frames_delete());
};
auto paste = [&]() { document.frames_deserialize(clipboard.get()); };
auto paste = [&]()
{
if (auto item = document.item_get())
{
document.snapshot("Paste Frame(s)");
std::set<int> indices{};
std::string errorString{};
auto start = reference.frameIndex + 1;
if (item->frames_deserialize(clipboard.get(), reference.itemType, start, indices, &errorString))
document.change(Document::FRAMES);
else
toasts.error(std::format("Failed to deserialize frame(s): {}", errorString));
}
else
toasts.error(std::format("Failed to deserialize frame(s): select an item first!"));
};
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 (shortcut(settings.shortcutCut, shortcut::FOCUSED)) cut();
if (shortcut(settings.shortcutCopy, shortcut::FOCUSED)) copy();
if (shortcut(settings.shortcutPaste, shortcut::FOCUSED)) paste();
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::BeginDisabled(hoveredFrame == anm2::REFERENCE_DEFAULT);
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")) paste();
ImGui::EndDisabled();
if (ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, hoveredFrame != anm2::Reference{})) cut();
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, hoveredFrame != anm2::Reference{})) copy();
if (ImGui::MenuItem("Paste", nullptr, false, !clipboard.is_empty())) paste();
ImGui::EndPopup();
}
@@ -165,7 +185,7 @@ namespace anm2ed::imgui
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) reference = itemReference;
}
ImGui::Image(resources.icons[icon].id, imgui::icon_size_get());
ImGui::Image(resources.icons[icon].id, icon_size_get());
ImGui::SameLine();
ImGui::TextUnformatted(label.c_str());
@@ -181,8 +201,8 @@ namespace anm2ed::imgui
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
int visibleIcon = isVisible ? icon::VISIBLE : icon::INVISIBLE;
if (ImGui::ImageButton("##Visible Toggle", resources.icons[visibleIcon].id, imgui::icon_size_get()))
document.item_visible_toggle(item);
if (ImGui::ImageButton("##Visible Toggle", resources.icons[visibleIcon].id, icon_size_get()))
DOCUMENT_EDIT(document, "Item Visibility", Document::FRAMES, isVisible = !isVisible);
ImGui::SetItemTooltip(isVisible ? "The item is shown. Press to hide." : "The item is hidden. Press to show.");
if (type == anm2::NULL_)
@@ -194,8 +214,8 @@ namespace anm2ed::imgui
ImGui::SetCursorPos(
ImVec2(itemSize.x - (ImGui::GetTextLineHeightWithSpacing() * 2) - ImGui::GetStyle().ItemSpacing.x,
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
if (ImGui::ImageButton("##Rect Toggle", resources.icons[rectIcon].id, imgui::icon_size_get()))
document.null_rect_toggle(null);
if (ImGui::ImageButton("##Rect Toggle", resources.icons[rectIcon].id, icon_size_get()))
DOCUMENT_EDIT(document, "Null Rect", Document::FRAMES, null.isShowRect = !null.isShowRect);
ImGui::SetItemTooltip(isShowRect ? "The null's rect is shown. Press to hide."
: "The null's rect is hidden. Press to show.");
}
@@ -217,7 +237,7 @@ namespace anm2ed::imgui
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
auto unusedIcon = isShowUnused ? icon::SHOW_UNUSED : icon::HIDE_UNUSED;
if (ImGui::ImageButton("##Unused Toggle", resources.icons[unusedIcon].id, imgui::icon_size_get()))
if (ImGui::ImageButton("##Unused Toggle", resources.icons[unusedIcon].id, icon_size_get()))
isShowUnused = !isShowUnused;
ImGui::SetItemTooltip(isShowUnused ? "Unused layers/nulls are shown. Press to hide."
: "Unused layers/nulls are hidden. Press to show.");
@@ -229,7 +249,7 @@ namespace anm2ed::imgui
ImVec2(itemSize.x - (ImGui::GetTextLineHeightWithSpacing() * 2) - ImGui::GetStyle().ItemSpacing.x,
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
if (ImGui::ImageButton("##Layers Toggle", resources.icons[layersIcon].id, imgui::icon_size_get()))
if (ImGui::ImageButton("##Layers Toggle", resources.icons[layersIcon].id, icon_size_get()))
isOnlyShowLayers = !isOnlyShowLayers;
ImGui::SetItemTooltip(isOnlyShowLayers ? "Only layers are visible. Press to show all items."
: "All items are visible. Press to only show layers.");
@@ -329,21 +349,30 @@ namespace anm2ed::imgui
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);
auto widgetSize = widget_size_with_row_get(2, ImGui::GetContentRegionAvail().x - style.WindowPadding.x);
ImGui::BeginDisabled(!animation);
{
imgui::shortcut(settings.shortcutAdd);
shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize)) propertiesPopup.open();
imgui::set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
ImGui::SameLine();
ImGui::BeginDisabled(!document.item_get() && reference.itemType != anm2::LAYER &&
reference.itemType != anm2::NULL_);
{
imgui::shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove", widgetSize)) document.item_remove(animation);
imgui::set_item_tooltip_shortcut("Remove the selected items from the animation.", settings.shortcutRemove);
shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove", widgetSize))
{
auto remove = [&]()
{
animation->item_remove(reference.itemType, reference.itemID);
reference = {reference.animationIndex};
};
DOCUMENT_EDIT(document, "Remove Item", Document::ITEMS, remove());
}
set_item_tooltip_shortcut("Remove the selected items from the animation.", settings.shortcutRemove);
}
ImGui::EndDisabled();
}
@@ -534,11 +563,12 @@ namespace anm2ed::imgui
if (ImGui::Button("##Frame Button", size))
{
if (type != anm2::TRIGGER && ImGui::IsKeyDown(ImGuiMod_Alt))
document.frame_is_interpolated_set(&frame, !frame.isInterpolated);
DOCUMENT_EDIT(document, "Frame Interpolation", Document::FRAMES,
frame.isInterpolated = !frame.isInterpolated);
if (type == anm2::LAYER)
{
document.referenceSpritesheet = anm2.content.layers[id].spritesheetID;
document.layersMultiSelect = {id};
document.spritesheet.reference = anm2.content.layers[id].spritesheetID;
document.layer.selection = {id};
}
reference = frameReference;
reference.frameTime = frameTime;
@@ -575,8 +605,8 @@ namespace anm2ed::imgui
Clipboard& clipboard)
{
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto& playback = document.playback;
auto& hoveredFrame = document.hoveredFrame;
auto itemsChildWidth = ImGui::GetTextLineHeightWithSpacing() * 15;
@@ -697,16 +727,16 @@ namespace anm2ed::imgui
ImGui::SetCursorPos(
ImVec2(ImGui::GetStyle().WindowPadding.x, ImGui::GetCursorPos().y + ImGui::GetStyle().ItemSpacing.y));
auto widgetSize = imgui::widget_size_with_row_get(10);
auto widgetSize = widget_size_with_row_get(10);
ImGui::BeginDisabled(!animation);
{
auto label = playback.isPlaying ? "Pause" : "Play";
auto tooltip = playback.isPlaying ? "Pause the animation." : "Play the animation.";
imgui::shortcut(settings.shortcutPlayPause);
shortcut(settings.shortcutPlayPause);
if (ImGui::Button(label, widgetSize)) playback.toggle();
imgui::set_item_tooltip_shortcut(tooltip, settings.shortcutPlayPause);
set_item_tooltip_shortcut(tooltip, settings.shortcutPlayPause);
ImGui::SameLine();
@@ -714,17 +744,45 @@ namespace anm2ed::imgui
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);
shortcut(settings.shortcutAdd);
if (ImGui::Button("Insert Frame", widgetSize))
{
auto insert_frame = [&]()
{
auto frame = document.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;
}
};
DOCUMENT_EDIT(document, "Insert Frame", Document::FRAMES, insert_frame());
}
set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd);
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);
shortcut(settings.shortcutRemove);
if (ImGui::Button("Delete Frame", widgetSize))
{
auto delete_frame = [&]()
{
item->frames.erase(item->frames.begin() + reference.frameIndex);
reference.frameIndex = glm::max(-1, --reference.frameIndex);
};
DOCUMENT_EDIT(document, "Delete Frame(s)", Document::FRAMES, delete_frame());
}
set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
ImGui::SameLine();
@@ -743,8 +801,8 @@ namespace anm2ed::imgui
ImGui::SameLine();
ImGui::SetNextItemWidth(widgetSize.x);
ImGui::InputInt("Animation Length", animation ? &animation->frameNum : &dummy_value<int>(), imgui::STEP,
imgui::STEP_FAST, !animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0);
ImGui::InputInt("Animation Length", animation ? &animation->frameNum : &dummy_value<int>(), STEP, STEP_FAST,
!animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0);
if (animation) animation->frameNum = clamp(animation->frameNum, anm2::FRAME_NUM_MIN, anm2::FRAME_NUM_MAX);
ImGui::SetItemTooltip("Set the animation's length.");
@@ -766,7 +824,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
ImGui::SetNextItemWidth(widgetSize.x);
imgui::input_text_string("Author", &anm2.info.createdBy);
input_text_string("Author", &anm2.info.createdBy);
ImGui::SetItemTooltip("Set the author of the document.");
ImGui::SameLine();
@@ -818,8 +876,8 @@ namespace anm2ed::imgui
isUnusedItemsSet = true;
}
auto footerSize = imgui::footer_size_get();
auto optionsSize = imgui::child_size_get(11);
auto footerSize = footer_size_get();
auto optionsSize = child_size_get(11);
auto itemsSize = ImVec2(0, ImGui::GetContentRegionAvail().y -
(optionsSize.y + footerSize.y + ImGui::GetStyle().ItemSpacing.y * 4));
if (ImGui::BeginChild("Options", optionsSize, ImGuiChildFlags_Borders))
@@ -890,12 +948,11 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(source == source::EXISTING);
{
imgui::input_text_string("Name", &addItemName);
input_text_string("Name", &addItemName);
ImGui::SetItemTooltip("Set the item's name.");
ImGui::BeginDisabled(type != anm2::LAYER);
{
auto spritesheets = anm2.spritesheet_names_get();
imgui::combo_strings("Spritesheet", &addItemSpritesheetID, spritesheets);
combo_negative_one_indexed("Spritesheet", &addItemSpritesheetID, document.spritesheet.labels);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
}
ImGui::EndDisabled();
@@ -934,11 +991,24 @@ namespace anm2ed::imgui
}
ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2);
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Add", widgetSize))
{
document.item_add((anm2::Type)type, addItemID, addItemName, (locale::Type)locale, addItemSpritesheetID);
anm2::Reference addReference{};
document.snapshot("Add Item");
if (type == anm2::LAYER)
addReference = anm2.layer_animation_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName,
addItemSpritesheetID - 1, (locale::Type)locale);
else if (type == anm2::NULL_)
addReference = anm2.null_animation_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName,
(locale::Type)locale);
document.change(Document::ITEMS);
reference = addReference;
item_properties_close();
}
ImGui::SetItemTooltip("Add the item, with the settings specified.");
@@ -961,7 +1031,7 @@ namespace anm2ed::imgui
auto frame = document.frame_get();
imgui::input_int_range("Interval", interval, anm2::FRAME_DELAY_MIN, frame ? frame->delay : anm2::FRAME_DELAY_MIN);
input_int_range("Interval", interval, anm2::FRAME_DELAY_MIN, frame ? frame->delay : anm2::FRAME_DELAY_MIN);
ImGui::SetItemTooltip("Set the maximum delay of each frame that will be baked.");
ImGui::Checkbox("Round Rotation", &isRoundRotation);
@@ -970,11 +1040,13 @@ namespace anm2ed::imgui
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);
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Bake", widgetSize))
{
document.frames_bake(interval, isRoundScale, isRoundRotation);
if (auto item = document.item_get())
DOCUMENT_EDIT(document, "Bake Frames", Document::FRAMES,
item->frames_bake(reference.frameIndex, interval, isRoundScale, isRoundRotation));
bakePopup.close();
}
ImGui::SetItemTooltip("Bake the selected frame(s) with the options selected.");
@@ -1009,24 +1081,41 @@ namespace anm2ed::imgui
popups(document, animation, settings);
if (imgui::shortcut(settings.shortcutPlayPause, shortcut::GLOBAL)) playback.toggle();
if (shortcut(settings.shortcutPlayPause, shortcut::GLOBAL)) playback.toggle();
if (animation)
{
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutPreviousFrame)))
if (chord_repeating(string_to_chord(settings.shortcutPreviousFrame)))
{
playback.decrement(settings.playbackIsClampPlayhead ? animation->frameNum : anm2::FRAME_NUM_MAX);
reference.frameTime = playback.time;
}
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutNextFrame)))
if (chord_repeating(string_to_chord(settings.shortcutNextFrame)))
{
playback.increment(settings.playbackIsClampPlayhead ? animation->frameNum : anm2::FRAME_NUM_MAX);
reference.frameTime = playback.time;
}
}
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutShortenFrame))) document.frame_shorten();
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutExtendFrame))) document.frame_extend();
if (ImGui::IsKeyChordPressed(string_to_chord(settings.shortcutShortenFrame))) document.snapshot("Shorten Frame");
if (chord_repeating(string_to_chord(settings.shortcutShortenFrame)))
{
if (auto frame = document.frame_get())
{
frame->shorten();
document.change(Document::FRAMES);
}
}
if (ImGui::IsKeyChordPressed(string_to_chord(settings.shortcutExtendFrame))) document.snapshot("Extend Frame");
if (chord_repeating(string_to_chord(settings.shortcutExtendFrame)))
{
if (auto frame = document.frame_get())
{
frame->extend();
document.change(Document::FRAMES);
}
}
}
}

View File

@@ -16,9 +16,9 @@ namespace anm2ed::imgui
PopupHelper propertiesPopup{PopupHelper("Item Properties", POPUP_NORMAL)};
PopupHelper bakePopup{PopupHelper("Bake", POPUP_TO_CONTENT)};
std::string addItemName{};
int addItemSpritesheetID{};
bool addItemIsRect{};
int addItemID{-1};
int addItemSpritesheetID{-1};
bool isUnusedItemsSet{};
std::set<int> unusedItems{};
glm::vec2 scroll{};

View File

@@ -31,12 +31,10 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("New", widgetSize)) dialog.file_open(dialog::ANM2_NEW); // handled in taskbar.cpp
ImGui::SameLine();
if (ImGui::Button("Open", widgetSize)) dialog.file_open(dialog::ANM2_OPEN); // handled in taskbar.cpp
if (ImGui::BeginChild("##Recent Files Child", ImVec2(), ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Recent Files Child", {}, ImGuiChildFlags_Borders))
{
for (auto [i, file] : std::views::enumerate(manager.recentFiles))
{