yeah these too
This commit is contained in:
@@ -19,7 +19,7 @@ namespace anm2ed::anm2
|
||||
{
|
||||
Anm2::Anm2() { info.createdOn = time::get("%m/%d/%Y %I:%M:%S %p"); }
|
||||
|
||||
Anm2::Anm2(const std::string& path, std::string* errorString)
|
||||
Anm2::Anm2(const std::filesystem::path& path, std::string* errorString)
|
||||
{
|
||||
XMLDocument document;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace anm2ed::anm2
|
||||
return element;
|
||||
}
|
||||
|
||||
bool Anm2::serialize(const std::string& path, std::string* errorString)
|
||||
bool Anm2::serialize(const std::filesystem::path& path, std::string* errorString)
|
||||
{
|
||||
XMLDocument document;
|
||||
document.InsertFirstChild(to_element(document));
|
||||
@@ -326,7 +326,8 @@ namespace anm2ed::anm2
|
||||
animations.items.push_back(std::move(processed));
|
||||
}
|
||||
|
||||
if (animations.defaultAnimation.empty() && !source.animations.defaultAnimation.empty()) {
|
||||
if (animations.defaultAnimation.empty() && !source.animations.defaultAnimation.empty())
|
||||
{
|
||||
animations.defaultAnimation = source.animations.defaultAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,16 +33,17 @@ namespace anm2ed::anm2
|
||||
|
||||
Anm2();
|
||||
tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&);
|
||||
bool serialize(const std::string&, std::string* = nullptr);
|
||||
bool serialize(const std::filesystem::path&, std::string* = nullptr);
|
||||
std::string to_string();
|
||||
Anm2(const std::string&, std::string* = nullptr);
|
||||
Anm2(const std::filesystem::path&, std::string* = nullptr);
|
||||
uint64_t hash();
|
||||
|
||||
Spritesheet* spritesheet_get(int);
|
||||
bool spritesheet_add(const std::string&, const std::string&, int&);
|
||||
bool spritesheet_add(const std::filesystem::path&, const std::filesystem::path&, int&);
|
||||
std::vector<std::string> spritesheet_labels_get();
|
||||
std::set<int> spritesheets_unused();
|
||||
bool spritesheets_deserialize(const std::string&, const std::string&, types::merge::Type type, std::string*);
|
||||
bool spritesheets_deserialize(const std::string&, const std::filesystem::path&, types::merge::Type type,
|
||||
std::string*);
|
||||
|
||||
void layer_add(int&);
|
||||
std::set<int> layers_unused();
|
||||
@@ -59,10 +60,10 @@ namespace anm2ed::anm2
|
||||
std::set<int> events_unused();
|
||||
bool events_deserialize(const std::string&, types::merge::Type, std::string*);
|
||||
|
||||
bool sound_add(const std::string& directory, const std::string& path, int& id);
|
||||
bool sound_add(const std::filesystem::path& directory, const std::filesystem::path& path, int& id);
|
||||
std::vector<std::string> sound_labels_get();
|
||||
std::set<int> sounds_unused();
|
||||
bool sounds_deserialize(const std::string&, const std::string&, types::merge::Type, std::string*);
|
||||
bool sounds_deserialize(const std::string&, const std::filesystem::path&, types::merge::Type, std::string*);
|
||||
|
||||
Animation* animation_get(int);
|
||||
std::vector<std::string> animation_labels_get();
|
||||
@@ -75,7 +76,6 @@ namespace anm2ed::anm2
|
||||
Reference null_animation_add(Reference = {}, std::string = {}, types::destination::Type = types::destination::ALL);
|
||||
|
||||
Frame* frame_get(int, Type, int, int = -1);
|
||||
void merge(const Anm2& source, const std::filesystem::path& destinationDirectory = {},
|
||||
const std::filesystem::path& sourceDirectory = {});
|
||||
void merge(const Anm2&, const std::filesystem::path&, const std::filesystem::path&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using namespace tinyxml2;
|
||||
|
||||
namespace anm2ed::anm2
|
||||
{
|
||||
bool Anm2::sound_add(const std::string& directory, const std::string& path, int& id)
|
||||
bool Anm2::sound_add(const std::filesystem::path& directory, const std::filesystem::path& path, int& id)
|
||||
{
|
||||
id = map::next_id_get(content.sounds);
|
||||
content.sounds[id] = Sound(directory, path);
|
||||
@@ -19,9 +19,12 @@ namespace anm2ed::anm2
|
||||
std::vector<std::string> Anm2::sound_labels_get()
|
||||
{
|
||||
std::vector<std::string> labels{};
|
||||
labels.emplace_back("None");
|
||||
labels.emplace_back(localize.get(BASIC_NONE));
|
||||
for (auto& [id, sound] : content.sounds)
|
||||
labels.emplace_back(sound.path.string());
|
||||
{
|
||||
auto pathCStr = sound.path.c_str();
|
||||
labels.emplace_back(std::vformat(localize.get(FORMAT_SOUND), std::make_format_args(id, pathCStr)));
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
@@ -39,7 +42,7 @@ namespace anm2ed::anm2
|
||||
return unused;
|
||||
}
|
||||
|
||||
bool Anm2::sounds_deserialize(const std::string& string, const std::string& directory, merge::Type type,
|
||||
bool Anm2::sounds_deserialize(const std::string& string, const std::filesystem::path& directory, merge::Type type,
|
||||
std::string* errorString)
|
||||
{
|
||||
XMLDocument document{};
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace anm2ed::anm2
|
||||
{
|
||||
Spritesheet* Anm2::spritesheet_get(int id) { return map::find(content.spritesheets, id); }
|
||||
|
||||
bool Anm2::spritesheet_add(const std::string& directory, const std::string& path, int& id)
|
||||
bool Anm2::spritesheet_add(const std::filesystem::path& directory, const std::filesystem::path& path, int& id)
|
||||
{
|
||||
Spritesheet spritesheet(directory, path);
|
||||
if (!spritesheet.is_valid()) return false;
|
||||
@@ -41,14 +41,14 @@ namespace anm2ed::anm2
|
||||
labels.emplace_back(localize.get(BASIC_NONE));
|
||||
for (auto& [id, spritesheet] : content.spritesheets)
|
||||
{
|
||||
auto string = spritesheet.path.string();
|
||||
labels.emplace_back(std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, string)));
|
||||
auto pathCStr = spritesheet.path.c_str();
|
||||
labels.emplace_back(std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, pathCStr)));
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
bool Anm2::spritesheets_deserialize(const std::string& string, const std::string& directory, merge::Type type,
|
||||
std::string* errorString)
|
||||
bool Anm2::spritesheets_deserialize(const std::string& string, const std::filesystem::path& directory,
|
||||
merge::Type type, std::string* errorString)
|
||||
{
|
||||
XMLDocument document{};
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ using namespace tinyxml2;
|
||||
|
||||
namespace anm2ed::anm2
|
||||
{
|
||||
Sound::Sound(const Sound& other) : path(other.path) { audio = path.empty() ? Audio() : Audio(path); }
|
||||
Sound::Sound(const Sound& other) : path(other.path), audio(other.audio) {}
|
||||
|
||||
Sound& Sound::operator=(const Sound& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
path = other.path;
|
||||
audio = path.empty() ? Audio() : Audio(path);
|
||||
audio = other.audio;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace anm2ed::anm2
|
||||
}
|
||||
}
|
||||
|
||||
Sound::Sound(const std::string& directory, const std::string& path)
|
||||
Sound::Sound(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
@@ -41,11 +41,6 @@ namespace anm2ed::anm2
|
||||
audio = Audio(this->path);
|
||||
}
|
||||
|
||||
Sound::Sound(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
: Sound(directory.string(), path.string())
|
||||
{
|
||||
}
|
||||
|
||||
Sound::Sound(XMLElement* element, int& id)
|
||||
{
|
||||
if (!element) return;
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace anm2ed::anm2
|
||||
Sound(const Sound&);
|
||||
Sound& operator=(const Sound&);
|
||||
Sound(tinyxml2::XMLElement*, int&);
|
||||
Sound(const std::string&, const std::string&);
|
||||
Sound(const std::filesystem::path&, const std::filesystem::path&);
|
||||
tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&, int);
|
||||
std::string to_string(int);
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace anm2ed::anm2
|
||||
}
|
||||
}
|
||||
|
||||
Spritesheet::Spritesheet(const std::string& directory, const std::string& path)
|
||||
Spritesheet::Spritesheet(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
@@ -41,11 +41,6 @@ namespace anm2ed::anm2
|
||||
texture = Texture(this->path);
|
||||
}
|
||||
|
||||
Spritesheet::Spritesheet(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
: Spritesheet(directory.string(), path.string())
|
||||
{
|
||||
}
|
||||
|
||||
XMLElement* Spritesheet::to_element(XMLDocument& document, int id)
|
||||
{
|
||||
auto element = document.NewElement("Spritesheet");
|
||||
@@ -67,7 +62,7 @@ namespace anm2ed::anm2
|
||||
return xml::document_to_string(document);
|
||||
}
|
||||
|
||||
bool Spritesheet::save(const std::string& directory, const std::string& path)
|
||||
bool Spritesheet::save(const std::filesystem::path& directory, const std::filesystem::path& path)
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(directory);
|
||||
this->path = !path.empty() ? make_relative_or_keep(path) : this->path;
|
||||
|
||||
@@ -16,11 +16,10 @@ namespace anm2ed::anm2
|
||||
|
||||
Spritesheet() = default;
|
||||
Spritesheet(tinyxml2::XMLElement*, int&);
|
||||
Spritesheet(const std::string&, const std::string& = {});
|
||||
Spritesheet(const std::filesystem::path&, const std::filesystem::path& = {});
|
||||
tinyxml2::XMLElement* to_element(tinyxml2::XMLDocument&, int);
|
||||
std::string to_string(int id);
|
||||
bool save(const std::string&, const std::string& = {});
|
||||
bool save(const std::filesystem::path&, const std::filesystem::path& = {});
|
||||
void serialize(tinyxml2::XMLDocument&, tinyxml2::XMLElement*, int);
|
||||
void reload(const std::filesystem::path&);
|
||||
bool is_valid();
|
||||
|
||||
@@ -58,12 +58,12 @@ namespace anm2ed
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
void Dialog::file_explorer_open(const std::string& path)
|
||||
void Dialog::file_explorer_open(const std::filesystem::path& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ShellExecuteA(NULL, "open", path.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
#elif __unix__
|
||||
system(std::format("xdg-open \"{}\" &", path).c_str());
|
||||
system(std::format("xdg-open \"{}\" &", path.c_str()).c_str());
|
||||
#else
|
||||
toasts.push(localize.get(TOAST_NOT_SUPPORTED));
|
||||
logger.warning(localize.get(TOAST_NOT_SUPPORTED, anm2ed::ENGLISH));
|
||||
@@ -74,11 +74,4 @@ namespace anm2ed
|
||||
|
||||
bool Dialog::is_selected(dialog::Type type) const { return this->type == type && !path.empty(); }
|
||||
|
||||
void Dialog::set_string_to_selected_path(std::string& string, dialog::Type type)
|
||||
{
|
||||
if (type == NONE) return;
|
||||
if (!is_selected(type)) return;
|
||||
string = path;
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace anm2ed::dialog
|
||||
X(ANM2_OPEN, ANM2) \
|
||||
X(ANM2_SAVE, ANM2) \
|
||||
X(SOUND_OPEN, SOUND) \
|
||||
X(SOUND_REPLACE, SOUND) \
|
||||
X(SPRITESHEET_OPEN, PNG) \
|
||||
X(SPRITESHEET_REPLACE, PNG) \
|
||||
X(FFMPEG_PATH_SET, EXECUTABLE) \
|
||||
@@ -75,7 +76,7 @@ namespace anm2ed
|
||||
{
|
||||
public:
|
||||
SDL_Window* window{};
|
||||
std::string path{};
|
||||
std::filesystem::path path{};
|
||||
dialog::Type type{dialog::NONE};
|
||||
int selectedFilter{-1};
|
||||
|
||||
@@ -86,7 +87,6 @@ namespace anm2ed
|
||||
void folder_open(dialog::Type type);
|
||||
bool is_selected(dialog::Type type) const;
|
||||
void reset();
|
||||
void file_explorer_open(const std::string&);
|
||||
void set_string_to_selected_path(std::string& set, dialog::Type type);
|
||||
void file_explorer_open(const std::filesystem::path&);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,8 +154,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (ImGui::Selectable(label.c_str(), isSelected, flags)) isActivated = true;
|
||||
|
||||
if (state == RENAME_FORCE_EDIT || (ImGui::IsWindowFocused() && ImGui::IsKeyPressed(ImGuiKey_F2)) ||
|
||||
(ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)))
|
||||
if (state == RENAME_FORCE_EDIT || (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)))
|
||||
{
|
||||
state = RENAME_BEGIN;
|
||||
editID = id;
|
||||
|
||||
@@ -206,9 +206,8 @@ namespace anm2ed::imgui
|
||||
using std::set<int>::erase;
|
||||
|
||||
MultiSelectStorage();
|
||||
void start(size_t, ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_BoxSelect2d |
|
||||
ImGuiMultiSelectFlags_ClearOnEscape |
|
||||
ImGuiMultiSelectFlags_ScopeWindow);
|
||||
void start(size_t,
|
||||
ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ScopeWindow);
|
||||
void apply();
|
||||
void finish();
|
||||
void set_index_map(std::vector<int>*);
|
||||
|
||||
@@ -742,7 +742,12 @@ namespace anm2ed::imgui
|
||||
ImGui::SameLine();
|
||||
input_text_string(localize.get(LABEL_FFMPEG_PATH), &ffmpegPath);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FFMPEG_PATH));
|
||||
dialog.set_string_to_selected_path(ffmpegPath, dialog::FFMPEG_PATH_SET);
|
||||
|
||||
if (dialog.is_selected(dialog::FFMPEG_PATH_SET))
|
||||
{
|
||||
ffmpegPath = dialog.path;
|
||||
dialog.reset();
|
||||
}
|
||||
|
||||
if (ImGui::ImageButton("##Path Set", resources.icons[icon::FOLDER].id, icon_size_get()))
|
||||
{
|
||||
@@ -755,7 +760,12 @@ namespace anm2ed::imgui
|
||||
auto pathLabel = type == render::PNGS ? LABEL_OUTPUT_DIRECTORY : LABEL_OUTPUT_PATH;
|
||||
input_text_string(localize.get(pathLabel), &path);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_OUTPUT_PATH));
|
||||
dialog.set_string_to_selected_path(path, dialogType);
|
||||
|
||||
if (dialog.is_selected(dialogType))
|
||||
{
|
||||
path = dialog.path;
|
||||
dialog.reset();
|
||||
}
|
||||
|
||||
if (ImGui::Combo(localize.get(LABEL_TYPE), &type, render::STRINGS, render::COUNT)) render_set();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RENDER_TYPE));
|
||||
|
||||
@@ -275,6 +275,22 @@ namespace anm2ed::imgui
|
||||
|
||||
auto center_view = [&]() { pan = vec2(); };
|
||||
|
||||
auto fit_view = [&]()
|
||||
{
|
||||
if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform));
|
||||
};
|
||||
|
||||
auto zoom_adjust = [&](float delta)
|
||||
{
|
||||
auto focus = position_translate(zoom, pan, size * 0.5f);
|
||||
auto previousZoom = zoom;
|
||||
zoom_set(zoom, pan, focus, delta);
|
||||
if (zoom != previousZoom) hasPendingZoomPanAdjust = true;
|
||||
};
|
||||
|
||||
auto zoom_in = [&]() { zoom_adjust(zoomStep); };
|
||||
auto zoom_out = [&]() { zoom_adjust(-zoomStep); };
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_ANIMATION_PREVIEW_WINDOW), &settings.windowIsAnimationPreview))
|
||||
{
|
||||
auto childSize = ImVec2(row_widget_width_get(4),
|
||||
@@ -306,14 +322,13 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_CENTER_VIEW]);
|
||||
if (ImGui::Button(localize.get(LABEL_CENTER_VIEW), widgetSize)) pan = vec2();
|
||||
if (ImGui::Button(localize.get(LABEL_CENTER_VIEW), widgetSize)) center_view();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_CENTER_VIEW), settings.shortcutCenterView);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_FIT]);
|
||||
if (ImGui::Button(localize.get(LABEL_FIT), widgetSize))
|
||||
if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform));
|
||||
if (ImGui::Button(localize.get(LABEL_FIT), widgetSize)) fit_view();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_FIT), settings.shortcutFit);
|
||||
|
||||
auto mousePosInt = ivec2(mousePos);
|
||||
@@ -813,6 +828,29 @@ namespace anm2ed::imgui
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Animation Preview Context Menu", ImGuiMouseButton_Right))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(LABEL_CENTER_VIEW), settings.shortcutCenterView.c_str())) center_view();
|
||||
if (ImGui::MenuItem(localize.get(LABEL_FIT), settings.shortcutFit.c_str(), false, animation)) fit_view();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ZOOM_IN), settings.shortcutZoomIn.c_str())) zoom_in();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ZOOM_OUT), settings.shortcutZoomOut.c_str())) zoom_out();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
manager.progressPopup.trigger();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "animations.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <ranges>
|
||||
|
||||
@@ -20,27 +19,185 @@ namespace anm2ed::imgui
|
||||
auto& document = *manager.get();
|
||||
auto& anm2 = document.anm2;
|
||||
auto& reference = document.reference;
|
||||
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;
|
||||
auto rename_format_get = [&](int index)
|
||||
{ return std::format("###Document #{} Animation #{}", manager.selected, index); };
|
||||
|
||||
auto animations_remove = [&]()
|
||||
auto rename = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
if (!selection.empty()) renameQueued = *selection.begin();
|
||||
};
|
||||
|
||||
auto add = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto it = selection.rbegin(); it != selection.rend(); ++it)
|
||||
anm2::Animation animation;
|
||||
animation.name = localize.get(TEXT_NEW_ANIMATION);
|
||||
if (anm2::Animation* referenceAnimation = document.animation_get())
|
||||
{
|
||||
auto i = *it;
|
||||
if (overlayIndex == i) overlayIndex = -1;
|
||||
if (reference.animationIndex == i) reference.animationIndex = -1;
|
||||
anm2.animations.items.erase(anm2.animations.items.begin() + i);
|
||||
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();
|
||||
}
|
||||
selection.clear();
|
||||
}
|
||||
animation.rootAnimation.frames.emplace_back(anm2::Frame());
|
||||
|
||||
auto index = (int)anm2.animations.items.size();
|
||||
if (!selection.empty())
|
||||
{
|
||||
index = *selection.rbegin() + 1;
|
||||
index = std::min(index, (int)anm2.animations.items.size());
|
||||
}
|
||||
|
||||
if (anm2.animations.items.empty()) anm2.animations.defaultAnimation = animation.name;
|
||||
|
||||
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
|
||||
selection = {index};
|
||||
reference = {index};
|
||||
newAnimationSelectedIndex = index;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_ANIMATION), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
auto remove = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
for (auto it = selection.rbegin(); it != selection.rend(); ++it)
|
||||
{
|
||||
auto i = *it;
|
||||
if (overlayIndex == i) overlayIndex = -1;
|
||||
if (reference.animationIndex == i) reference.animationIndex = -1;
|
||||
anm2.animations.items.erase(anm2.animations.items.begin() + i);
|
||||
}
|
||||
selection.clear();
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_ANIMATIONS), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
auto duplicate = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
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, localize.get(EDIT_REMOVE_ANIMATIONS), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
auto merge = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
if (mergeSelection.contains(overlayIndex)) overlayIndex = -1;
|
||||
auto merged = anm2.animations_merge(mergeReference, mergeSelection, (merge::Type)settings.mergeType,
|
||||
settings.mergeIsDeleteAnimationsAfter);
|
||||
|
||||
selection = {merged};
|
||||
reference = {merged};
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
auto merge_popup_open = [&]()
|
||||
{
|
||||
mergePopup.open();
|
||||
mergeSelection.clear();
|
||||
mergeReference = *selection.begin();
|
||||
};
|
||||
|
||||
auto merge_quick = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
int merged{};
|
||||
if (selection.contains(overlayIndex)) overlayIndex = -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, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
auto default_set = [&]()
|
||||
{
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_DEFAULT_ANIMATION), Document::ANIMATIONS,
|
||||
anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name);
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& i : selection)
|
||||
clipboardText += anm2.animations.items[i].to_string();
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto cut = [&]()
|
||||
{
|
||||
copy();
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_CUT_ANIMATIONS), Document::ANIMATIONS, remove());
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto clipboardText = clipboard.get();
|
||||
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.push(
|
||||
std::vformat(localize.get(TOAST_DESERIALIZE_ANIMATIONS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_ANIMATIONS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_ANIMATIONS), Document::ANIMATIONS, behavior());
|
||||
};
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_ANIMATIONS_WINDOW), &settings.windowIsAnimations))
|
||||
@@ -70,9 +227,13 @@ namespace anm2ed::imgui
|
||||
ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
|
||||
ImGui::SetNextItemSelectionUserData((int)i);
|
||||
|
||||
if (isNewAnimation) renameState = RENAME_FORCE_EDIT;
|
||||
if (selectable_input_text(animation.name, std::format("###Document #{} Animation #{}", manager.selected, i),
|
||||
animation.name, selection.contains((int)i), ImGuiSelectableFlags_None, renameState))
|
||||
if (isNewAnimation || renameQueued == i)
|
||||
{
|
||||
renameState = RENAME_FORCE_EDIT;
|
||||
renameQueued = -1;
|
||||
}
|
||||
if (selectable_input_text(animation.name, rename_format_get(i), animation.name, selection.contains((int)i),
|
||||
ImGuiSelectableFlags_None, renameState))
|
||||
{
|
||||
reference = {(int)i};
|
||||
document.frames.clear();
|
||||
@@ -85,7 +246,6 @@ namespace anm2ed::imgui
|
||||
document.change(Document::ANIMATIONS);
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) hovered = (int)i;
|
||||
|
||||
if (isNewAnimation)
|
||||
{
|
||||
@@ -145,68 +305,42 @@ namespace anm2ed::imgui
|
||||
|
||||
selection.finish();
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& i : selection)
|
||||
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();
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_CUT_ANIMATIONS), Document::ANIMATIONS, animations_remove());
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
auto clipboardText = clipboard.get();
|
||||
|
||||
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.push(std::vformat(localize.get(TOAST_DESERIALIZE_ANIMATIONS_FAILED),
|
||||
std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_ANIMATIONS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_ANIMATIONS), Document::ANIMATIONS, deserialize());
|
||||
};
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_RENAME], shortcut::FOCUSED)) rename();
|
||||
if (shortcut(manager.chords[SHORTCUT_MERGE], shortcut::FOCUSED)) merge_quick();
|
||||
if (shortcut(manager.chords[SHORTCUT_CUT], shortcut::FOCUSED)) cut();
|
||||
if (shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false,
|
||||
!selection.empty() || hovered > -1))
|
||||
{
|
||||
cut();
|
||||
}
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
|
||||
!selection.empty() || hovered > -1))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_RENAME), settings.shortcutRename.c_str(), false,
|
||||
selection.size() == 1))
|
||||
rename();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ADD), settings.shortcutAdd.c_str())) add();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_DUPLICATE), settings.shortcutDuplicate.c_str())) duplicate();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_MERGE), settings.shortcutMerge.c_str())) merge_quick();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REMOVE), settings.shortcutRemove.c_str())) remove();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_DEFAULT), settings.shortcutDefault.c_str(), false,
|
||||
selection.size() == 1))
|
||||
default_set();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, !selection.empty())) cut();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty()))
|
||||
copy();
|
||||
}
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
{
|
||||
paste();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
@@ -215,133 +349,45 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(5);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
|
||||
{
|
||||
auto add = [&]()
|
||||
{
|
||||
anm2::Animation animation;
|
||||
animation.name = localize.get(TEXT_NEW_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 = (int)anm2.animations.items.size();
|
||||
if (!selection.empty())
|
||||
{
|
||||
index = *selection.rbegin() + 1;
|
||||
index = std::min(index, (int)anm2.animations.items.size());
|
||||
}
|
||||
|
||||
if (anm2.animations.items.empty()) anm2.animations.defaultAnimation = animation.name;
|
||||
|
||||
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
|
||||
selection = {index};
|
||||
reference = {index};
|
||||
newAnimationSelectedIndex = index;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_ANIMATION), Document::ANIMATIONS, add());
|
||||
}
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) add();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_ANIMATION), settings.shortcutAdd);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.empty());
|
||||
{
|
||||
shortcut(manager.chords[SHORTCUT_DUPLICATE]);
|
||||
if (ImGui::Button(localize.get(BASIC_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, localize.get(EDIT_DUPLICATE_ANIMATIONS), Document::ANIMATIONS, duplicate());
|
||||
}
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_DUPLICATE_ANIMATION), settings.shortcutDuplicate);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_MERGE], shortcut::FOCUSED) && !selection.empty())
|
||||
{
|
||||
auto merge_quick = [&]()
|
||||
{
|
||||
int merged{};
|
||||
if (selection.contains(overlayIndex)) overlayIndex = -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, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, merge_quick())
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
{
|
||||
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize))
|
||||
{
|
||||
mergePopup.open();
|
||||
mergeSelection.clear();
|
||||
mergeReference = *selection.begin();
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_OPEN_MERGE_POPUP), settings.shortcutMerge);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE), widgetSize))
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_ANIMATIONS), Document::ANIMATIONS, animations_remove());
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_ANIMATION), settings.shortcutRemove);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_DEFAULT]);
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
if (ImGui::Button(localize.get(BASIC_DEFAULT), widgetSize))
|
||||
{
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_DEFAULT_ANIMATION), Document::ANIMATIONS,
|
||||
anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_SET_DEFAULT_ANIMATION), settings.shortcutDefault);
|
||||
}
|
||||
shortcut(manager.chords[SHORTCUT_DUPLICATE]);
|
||||
if (ImGui::Button(localize.get(BASIC_DUPLICATE), widgetSize)) duplicate();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_DUPLICATE_ANIMATION), settings.shortcutDuplicate);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize)) merge_popup_open();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_OPEN_MERGE_POPUP), settings.shortcutMerge);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.empty());
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE), widgetSize)) remove();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_ANIMATION), settings.shortcutRemove);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
shortcut(manager.chords[SHORTCUT_DEFAULT]);
|
||||
if (ImGui::Button(localize.get(BASIC_DEFAULT), widgetSize)) default_set();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_SET_DEFAULT_ANIMATION), settings.shortcutDefault);
|
||||
|
||||
mergePopup.trigger();
|
||||
|
||||
if (ImGui::BeginPopupModal(mergePopup.label(), &mergePopup.isOpen, ImGuiWindowFlags_NoResize))
|
||||
{
|
||||
auto merge_close = [&]()
|
||||
auto close = [&]()
|
||||
{
|
||||
mergeSelection.clear();
|
||||
mergePopup.close();
|
||||
@@ -361,16 +407,16 @@ namespace anm2ed::imgui
|
||||
{
|
||||
mergeSelection.start(anm2.animations.items.size());
|
||||
|
||||
for (std::size_t index = 0; index < anm2.animations.items.size(); ++index)
|
||||
for (int i = 0; i < (int)anm2.animations.items.size(); i++)
|
||||
{
|
||||
if ((int)index == mergeReference) continue;
|
||||
if (i == mergeReference) continue;
|
||||
|
||||
auto& animation = anm2.animations.items[index];
|
||||
auto& animation = anm2.animations.items[i];
|
||||
|
||||
ImGui::PushID((int)index);
|
||||
ImGui::PushID(i);
|
||||
|
||||
ImGui::SetNextItemSelectionUserData((int)index);
|
||||
ImGui::Selectable(animation.name.c_str(), mergeSelection.contains((int)index));
|
||||
ImGui::SetNextItemSelectionUserData(i);
|
||||
ImGui::Selectable(animation.name.c_str(), mergeSelection.contains(i));
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
@@ -408,30 +454,19 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
ImGui::BeginDisabled(mergeSelection.empty());
|
||||
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize))
|
||||
{
|
||||
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize))
|
||||
{
|
||||
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, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, merge());
|
||||
merge_close();
|
||||
}
|
||||
merge();
|
||||
close();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(localize.get(LABEL_CLOSE), widgetSize)) merge_close();
|
||||
if (ImGui::Button(localize.get(LABEL_CLOSE), widgetSize)) close();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace anm2ed::imgui
|
||||
{
|
||||
PopupHelper mergePopup{PopupHelper(LABEL_ANIMATIONS_MERGE_POPUP)};
|
||||
int newAnimationSelectedIndex{-1};
|
||||
int renameQueued{-1};
|
||||
bool isInContextMenu{};
|
||||
RenameState renameState{RENAME_SELECTABLE};
|
||||
|
||||
public:
|
||||
|
||||
@@ -18,11 +18,75 @@ namespace anm2ed::imgui
|
||||
auto& document = *manager.get();
|
||||
auto& anm2 = document.anm2;
|
||||
auto& unused = document.event.unused;
|
||||
auto& hovered = document.event.hovered;
|
||||
auto& reference = document.event.reference;
|
||||
auto& selection = document.event.selection;
|
||||
|
||||
hovered = -1;
|
||||
auto rename_format_get = [&](int id) { return std::format("###Document #{} Event #{}", manager.selected, id); };
|
||||
auto rename = [&]()
|
||||
{
|
||||
if (!selection.empty()) renameQueued = *selection.begin();
|
||||
};
|
||||
|
||||
auto add = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto id = map::next_id_get(anm2.content.events);
|
||||
anm2::Event event{};
|
||||
event.name = localize.get(TEXT_NEW_EVENT);
|
||||
anm2.content.events[id] = event;
|
||||
selection = {id};
|
||||
reference = {id};
|
||||
newEventId = id;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_EVENT), Document::EVENTS, behavior());
|
||||
};
|
||||
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
if (unused.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.events.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_EVENTS), Document::EVENTS, behavior());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.events[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_EVENTS));
|
||||
if (anm2.events_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
document.change(Document::EVENTS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_EVENTS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_EVENTS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_EVENTS), Document::EVENTS, behavior());
|
||||
};
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_EVENTS_WINDOW), &settings.windowIsEvents))
|
||||
{
|
||||
@@ -38,16 +102,19 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::PushID(id);
|
||||
ImGui::SetNextItemSelectionUserData(id);
|
||||
if (isNewEvent) renameState = RENAME_FORCE_EDIT;
|
||||
if (selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
|
||||
event.name, selection.contains(id), ImGuiSelectableFlags_None, renameState))
|
||||
if (isNewEvent || renameQueued == id)
|
||||
{
|
||||
renameState = RENAME_FORCE_EDIT;
|
||||
renameQueued = -1;
|
||||
}
|
||||
if (selectable_input_text(event.name, rename_format_get(id), event.name, selection.contains(id),
|
||||
ImGuiSelectableFlags_None, renameState))
|
||||
{
|
||||
if (renameState == RENAME_BEGIN)
|
||||
document.snapshot(localize.get(EDIT_RENAME_EVENT));
|
||||
else if (renameState == RENAME_FINISHED)
|
||||
document.change(Document::EVENTS);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) hovered = id;
|
||||
|
||||
if (isNewEvent)
|
||||
{
|
||||
@@ -68,51 +135,39 @@ namespace anm2ed::imgui
|
||||
|
||||
selection.finish();
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
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)
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_EVENTS));
|
||||
if (anm2.events_deserialize(clipboard.get(), type, &errorString))
|
||||
document.change(Document::EVENTS);
|
||||
else
|
||||
{
|
||||
toasts.push(
|
||||
std::vformat(localize.get(TOAST_DESERIALIZE_EVENTS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_EVENTS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_RENAME], shortcut::FOCUSED)) rename();
|
||||
if (shortcut(manager.chords[SHORTCUT_ADD], shortcut::FOCUSED)) add();
|
||||
if (shortcut(manager.chords[SHORTCUT_REMOVE], shortcut::FOCUSED)) remove_unused();
|
||||
if (shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste(merge::APPEND);
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||
{
|
||||
ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, false);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
|
||||
!selection.empty() || hovered > -1))
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_RENAME), settings.shortcutRename.c_str(), false,
|
||||
selection.size() == 1))
|
||||
rename();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ADD), settings.shortcutAdd.c_str())) add();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str(), false,
|
||||
!unused.empty()))
|
||||
remove_unused();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty()))
|
||||
copy();
|
||||
|
||||
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_APPEND), settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE))) paste(merge::REPLACE);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
paste();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@@ -122,37 +177,13 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
|
||||
{
|
||||
auto add = [&]()
|
||||
{
|
||||
auto id = map::next_id_get(anm2.content.events);
|
||||
anm2::Event event{};
|
||||
event.name = localize.get(TEXT_NEW_EVENT);
|
||||
anm2.content.events[id] = event;
|
||||
selection = {id};
|
||||
reference = {id};
|
||||
newEventId = id;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_EVENT), Document::EVENTS, add());
|
||||
}
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) add();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_EVENT), settings.shortcutAdd);
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
ImGui::BeginDisabled(unused.empty());
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
|
||||
{
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.events.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_EVENTS), Document::EVENTS, remove_unused());
|
||||
}
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize)) remove_unused();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_EVENTS), settings.shortcutRemove);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace anm2ed::imgui
|
||||
class Events
|
||||
{
|
||||
int newEventId{-1};
|
||||
int renameQueued{-1};
|
||||
RenameState renameState{RENAME_SELECTABLE};
|
||||
|
||||
public:
|
||||
|
||||
@@ -19,11 +19,56 @@ namespace anm2ed::imgui
|
||||
auto& anm2 = document.anm2;
|
||||
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;
|
||||
auto add = [&]() { manager.layer_properties_open(); };
|
||||
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
if (unused.empty()) return;
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.layers.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_LAYERS), Document::LAYERS, behavior());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.layers[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_LAYERS));
|
||||
if (anm2.layers_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
document.change(Document::NULLS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_LAYERS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_LAYERS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_LAYERS), Document::LAYERS, behavior());
|
||||
};
|
||||
|
||||
auto properties = [&](int id) { manager.layer_properties_open(id); };
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_LAYERS_WINDOW), &settings.windowIsLayers))
|
||||
{
|
||||
@@ -49,13 +94,7 @@ namespace anm2ed::imgui
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newLayerId = -1;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
hovered = id;
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) manager.layer_properties_open(id);
|
||||
}
|
||||
else
|
||||
hovered = -1;
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) properties(id);
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
@@ -72,51 +111,37 @@ namespace anm2ed::imgui
|
||||
|
||||
selection.finish();
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
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)
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_LAYERS));
|
||||
if (anm2.layers_deserialize(clipboard.get(), type, &errorString))
|
||||
document.change(Document::NULLS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_LAYERS_FAILED),
|
||||
std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_LAYERS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_ADD], shortcut::FOCUSED)) add();
|
||||
if (shortcut(manager.chords[SHORTCUT_REMOVE], shortcut::FOCUSED)) remove_unused();
|
||||
if (shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste(merge::APPEND);
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||
{
|
||||
ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, false);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
|
||||
!selection.empty() || hovered > -1))
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PROPERTIES), nullptr, false, selection.size() == 1))
|
||||
properties(*selection.begin());
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str(), false,
|
||||
!unused.empty()))
|
||||
remove_unused();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty()))
|
||||
copy();
|
||||
|
||||
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_APPEND), settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE))) paste(merge::REPLACE);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
paste();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@@ -126,23 +151,13 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) manager.layer_properties_open();
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) add();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_LAYER), settings.shortcutAdd);
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
ImGui::BeginDisabled(unused.empty());
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
|
||||
{
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.layers.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_LAYERS), Document::LAYERS, remove_unused());
|
||||
}
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize)) remove_unused();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_LAYERS), settings.shortcutRemove);
|
||||
}
|
||||
@@ -171,7 +186,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (reference == -1)
|
||||
{
|
||||
auto add = [&]()
|
||||
auto layer_add = [&]()
|
||||
{
|
||||
auto id = map::next_id_get(anm2.content.layers);
|
||||
anm2.content.layers[id] = layer;
|
||||
@@ -179,17 +194,17 @@ namespace anm2ed::imgui
|
||||
newLayerId = id;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_LAYER), Document::LAYERS, add());
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_LAYER), Document::LAYERS, layer_add());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto set = [&]()
|
||||
auto layer_set = [&]()
|
||||
{
|
||||
anm2.content.layers[reference] = layer;
|
||||
selection = {reference};
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_SET_LAYER_PROPERTIES), Document::LAYERS, set());
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_SET_LAYER_PROPERTIES), Document::LAYERS, layer_set());
|
||||
}
|
||||
|
||||
manager.layer_properties_close();
|
||||
|
||||
@@ -19,11 +19,56 @@ namespace anm2ed::imgui
|
||||
auto& anm2 = document.anm2;
|
||||
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;
|
||||
auto add = [&]() { manager.null_properties_open(); };
|
||||
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
if (unused.empty()) return;
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.nulls.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_NULLS), Document::NULLS, behavior());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.nulls[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_NULLS));
|
||||
if (anm2.nulls_deserialize(clipboard.get(), merge::APPEND, &errorString))
|
||||
document.change(Document::NULLS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_NULLS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_NULLS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_NULLS), Document::NULLS, behavior());
|
||||
};
|
||||
|
||||
auto properties = [&](int id) { manager.null_properties_open(id); };
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_NULLS_WINDOW), &settings.windowIsNulls))
|
||||
{
|
||||
@@ -48,11 +93,7 @@ namespace anm2ed::imgui
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newNullId = -1;
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
hovered = id;
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) manager.null_properties_open(id);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) properties(id);
|
||||
|
||||
if (isReferenced) ImGui::PopFont();
|
||||
|
||||
@@ -69,50 +110,37 @@ namespace anm2ed::imgui
|
||||
|
||||
selection.finish();
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
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)
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_NULLS));
|
||||
if (anm2.nulls_deserialize(clipboard.get(), type, &errorString))
|
||||
document.change(Document::NULLS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_NULLS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_NULLS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_ADD], shortcut::FOCUSED)) add();
|
||||
if (shortcut(manager.chords[SHORTCUT_REMOVE], shortcut::FOCUSED)) remove_unused();
|
||||
if (shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste(merge::APPEND);
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||
{
|
||||
ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, false);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
|
||||
selection.empty() || hovered > -1))
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PROPERTIES), nullptr, false, selection.size() == 1))
|
||||
properties(*selection.begin());
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str(), false,
|
||||
!unused.empty()))
|
||||
remove_unused();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty()))
|
||||
copy();
|
||||
|
||||
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_APPEND), settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE))) paste(merge::REPLACE);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
paste();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
@@ -122,23 +150,13 @@ namespace anm2ed::imgui
|
||||
auto widgetSize = widget_size_with_row_get(2);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) manager.null_properties_open();
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) add();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_NULL), settings.shortcutAdd);
|
||||
ImGui::SameLine();
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
ImGui::BeginDisabled(unused.empty());
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
|
||||
{
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.nulls.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_NULLS), Document::EVENTS, remove_unused());
|
||||
}
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize)) remove_unused();
|
||||
ImGui::EndDisabled();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_NULLS), settings.shortcutRemove);
|
||||
}
|
||||
@@ -168,7 +186,7 @@ namespace anm2ed::imgui
|
||||
{
|
||||
if (reference == -1)
|
||||
{
|
||||
auto add = [&]()
|
||||
auto null_add = [&]()
|
||||
{
|
||||
auto id = map::next_id_get(anm2.content.nulls);
|
||||
anm2.content.nulls[id] = null;
|
||||
@@ -176,17 +194,17 @@ namespace anm2ed::imgui
|
||||
newNullId = id;
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_NULL), Document::NULLS, add());
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_NULL), Document::NULLS, null_add());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto set = [&]()
|
||||
auto null_set = [&]()
|
||||
{
|
||||
anm2.content.nulls[reference] = null;
|
||||
selection = {reference};
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_SET_NULL_PROPERTIES), Document::NULLS, set());
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_SET_NULL_PROPERTIES), Document::NULLS, null_set());
|
||||
}
|
||||
|
||||
manager.null_properties_close();
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "filesystem_.h"
|
||||
#include "log.h"
|
||||
#include "strings.h"
|
||||
#include "toast.h"
|
||||
|
||||
using namespace anm2ed::dialog;
|
||||
using namespace anm2ed::util;
|
||||
using namespace anm2ed::types;
|
||||
using namespace anm2ed::resource;
|
||||
using namespace glm;
|
||||
|
||||
namespace anm2ed::imgui
|
||||
{
|
||||
@@ -18,157 +21,307 @@ namespace anm2ed::imgui
|
||||
auto& anm2 = document.anm2;
|
||||
auto& reference = document.sound.reference;
|
||||
auto& unused = document.sound.unused;
|
||||
auto& hovered = document.null.hovered;
|
||||
auto& selection = document.sound.selection;
|
||||
auto style = ImGui::GetStyle();
|
||||
|
||||
hovered = -1;
|
||||
auto add_open = [&]() { dialog.file_open(dialog::SOUND_OPEN); };
|
||||
auto replace_open = [&]() { dialog.file_open(dialog::SOUND_REPLACE); };
|
||||
|
||||
auto play = [&](anm2::Sound& sound) { sound.play(); };
|
||||
|
||||
auto add = [&](const std::filesystem::path& path)
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
int id{};
|
||||
auto pathString = path.string();
|
||||
if (anm2.sound_add(document.directory_get(), path, id))
|
||||
{
|
||||
selection = {id};
|
||||
newSoundId = id;
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZED), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_SOUND_INITIALIZED, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED), std::make_format_args(pathString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(pathString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_SOUND), Document::SOUNDS, behavior());
|
||||
};
|
||||
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
if (unused.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.sounds.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SOUNDS), Document::SOUNDS, behavior());
|
||||
};
|
||||
|
||||
auto reload = [&]()
|
||||
{
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : selection)
|
||||
{
|
||||
anm2::Sound& sound = anm2.content.sounds[id];
|
||||
sound.reload(document.directory_get());
|
||||
auto pathString = sound.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_RELOAD_SOUND), std::make_format_args(id, pathString)));
|
||||
logger.info(
|
||||
std::vformat(localize.get(TOAST_RELOAD_SOUND, anm2ed::ENGLISH), std::make_format_args(id, pathString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_RELOAD_SOUNDS), Document::SOUNDS, behavior());
|
||||
};
|
||||
|
||||
auto replace = [&](const std::filesystem::path& path)
|
||||
{
|
||||
if (selection.size() != 1 || path.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto& id = *selection.begin();
|
||||
anm2::Sound& sound = anm2.content.sounds[id];
|
||||
sound = anm2::Sound(document.directory_get().string(), path);
|
||||
auto pathString = sound.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SOUND), std::make_format_args(id, pathString)));
|
||||
logger.info(
|
||||
std::vformat(localize.get(TOAST_REPLACE_SOUND, anm2ed::ENGLISH), std::make_format_args(id, pathString)));
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REPLACE_SOUND), Document::SOUNDS, behavior());
|
||||
};
|
||||
|
||||
auto open_directory = [&](anm2::Sound& sound)
|
||||
{
|
||||
if (sound.path.empty()) return;
|
||||
filesystem::WorkingDirectory workingDirectory(document.directory_get());
|
||||
dialog.file_explorer_open(sound.path.parent_path());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.sounds[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(TOAST_SOUNDS_PASTE));
|
||||
if (anm2.sounds_deserialize(clipboard.get(), document.directory_get().string(), merge::APPEND, &errorString))
|
||||
document.change(Document::SOUNDS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUNDS_DESERIALIZE_ERROR), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SOUNDS_DESERIALIZE_ERROR, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_SOUNDS), Document::SOUNDS, behavior());
|
||||
};
|
||||
|
||||
auto context_menu = [&]()
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow) &&
|
||||
ImGui::IsMouseClicked(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("##Sound Context Menu");
|
||||
|
||||
if (ImGui::BeginPopup("##Sound Context Menu"))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(LABEL_PLAY), nullptr, false, selection.size() == 1))
|
||||
play(anm2.content.sounds[*selection.begin()]);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_OPEN_DIRECTORY), nullptr, false, selection.size() == 1))
|
||||
open_directory(anm2.content.sounds[*selection.begin()]);
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str(), false, !unused.empty()))
|
||||
remove_unused();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_RELOAD), nullptr, false, !selection.empty())) reload();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE), nullptr, false, selection.size() == 1)) replace_open();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty())) copy();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
paste();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
};
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_SOUNDS_WINDOW), &settings.windowIsSounds))
|
||||
{
|
||||
auto childSize = imgui::size_without_footer_get();
|
||||
|
||||
if (ImGui::BeginChild("##Sounds Child", childSize, true))
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
if (ImGui::BeginChild("##Sounds Child", childSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
auto soundChildSize = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() * 2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
|
||||
selection.start(anm2.content.sounds.size());
|
||||
|
||||
for (auto& [id, sound] : anm2.content.sounds)
|
||||
{
|
||||
auto isSelected = selection.contains(id);
|
||||
auto isReferenced = reference == id;
|
||||
const std::string pathString = sound.path.empty() ? std::string{anm2::NO_PATH} : sound.path.string();
|
||||
const char* pathLabel = pathString.c_str();
|
||||
|
||||
ImGui::PushID(id);
|
||||
ImGui::SetNextItemSelectionUserData(id);
|
||||
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
|
||||
if (ImGui::Selectable(pathLabel, isSelected)) sound.play();
|
||||
if (ImGui::IsItemHovered()) hovered = id;
|
||||
if (newSoundId == id)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSoundId = -1;
|
||||
}
|
||||
|
||||
if (isReferenced) ImGui::PopFont();
|
||||
|
||||
if (ImGui::BeginItemTooltip())
|
||||
if (ImGui::BeginChild("##Sound Child", soundChildSize, ImGuiChildFlags_Borders))
|
||||
{
|
||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||
ImGui::TextUnformatted(pathLabel);
|
||||
ImGui::PopFont();
|
||||
ImGui::Text("%s: %d", localize.get(BASIC_ID), id);
|
||||
ImGui::Text("%s", localize.get(TOOLTIP_SOUNDS_PLAY));
|
||||
if (!sound.is_valid())
|
||||
auto isSelected = selection.contains(id);
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
bool isValid = sound.is_valid();
|
||||
auto& soundIcon = isValid ? resources.icons[icon::SOUND] : resources.icons[icon::NONE];
|
||||
auto tintColor = !isValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
auto pathCStr = sound.path.c_str();
|
||||
|
||||
ImGui::SetNextItemSelectionUserData(id);
|
||||
ImGui::SetNextItemStorageID(id);
|
||||
if (ImGui::Selectable("##Sound Selectable", isSelected, 0, soundChildSize))
|
||||
{
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", localize.get(TOOLTIP_SOUND_INVALID));
|
||||
reference = id;
|
||||
play(sound);
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) open_directory(sound);
|
||||
if (newSoundId == id)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
newSoundId = -1;
|
||||
}
|
||||
|
||||
auto textWidth = ImGui::CalcTextSize(pathCStr).x;
|
||||
auto tooltipPadding = style.WindowPadding.x * 4.0f;
|
||||
auto minWidth = textWidth + style.ItemSpacing.x + tooltipPadding;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||
ImGui::SetNextWindowSize(ImVec2(minWidth, 0), ImGuiCond_Appearing);
|
||||
if (ImGui::BeginItemTooltip())
|
||||
{
|
||||
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
|
||||
ImGui::TextUnformatted(sound.path.c_str());
|
||||
ImGui::PopFont();
|
||||
ImGui::Text("%s: %d", localize.get(BASIC_ID), id);
|
||||
if (!isValid)
|
||||
{
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", localize.get(TOOLTIP_SOUND_INVALID));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("%s", localize.get(TEXT_SOUND_PLAY));
|
||||
ImGui::Text("%s", localize.get(TEXT_OPEN_DIRECTORY));
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImGui::SetCursorPos(cursorPos);
|
||||
auto imageSize = to_imvec2(vec2(soundChildSize.y));
|
||||
ImGui::ImageWithBg(soundIcon.id, imageSize, ImVec2(), ImVec2(1, 1), ImVec4(), tintColor);
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(soundChildSize.y + style.ItemSpacing.x,
|
||||
soundChildSize.y - soundChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
|
||||
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_SOUND), std::make_format_args(id, pathCStr)).c_str());
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
context_menu();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
selection.finish();
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.sounds[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
}
|
||||
else if (hovered > -1)
|
||||
clipboard.set(anm2.content.sounds[hovered].to_string(hovered));
|
||||
};
|
||||
|
||||
auto paste = [&](merge::Type type)
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(TOAST_SOUNDS_PASTE));
|
||||
if (anm2.sounds_deserialize(clipboard.get(), document.directory_get().string(), type, &errorString))
|
||||
document.change(Document::SOUNDS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUNDS_DESERIALIZE_ERROR), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SOUNDS_DESERIALIZE_ERROR, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste(merge::APPEND);
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
|
||||
{
|
||||
ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, false);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(),
|
||||
!selection.empty() && hovered > -1))
|
||||
copy();
|
||||
|
||||
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_APPEND), settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE))) paste(merge::REPLACE);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
auto widgetSize = imgui::widget_size_with_row_get(2);
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
auto widgetSize = imgui::widget_size_with_row_get(4);
|
||||
|
||||
imgui::shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) dialog.file_open(dialog::SOUND_OPEN);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) add_open();
|
||||
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_SOUND_ADD), settings.shortcutAdd);
|
||||
|
||||
if (dialog.is_selected(dialog::SOUND_OPEN))
|
||||
{
|
||||
add(dialog.path);
|
||||
dialog.reset();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
imgui::shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
ImGui::BeginDisabled(unused.empty());
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
|
||||
{
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
anm2.content.sounds.erase(id);
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SOUNDS), Document::SOUNDS, remove_unused());
|
||||
};
|
||||
ImGui::EndDisabled();
|
||||
imgui::shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize)) remove_unused();
|
||||
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_SOUNDS), settings.shortcutRemove);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.empty());
|
||||
if (ImGui::Button(localize.get(BASIC_RELOAD), widgetSize)) reload();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RELOAD_SOUNDS));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
if (ImGui::Button(localize.get(BASIC_REPLACE), widgetSize)) replace_open();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPLACE_SOUND));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (dialog.is_selected(dialog::SOUND_REPLACE))
|
||||
{
|
||||
replace(dialog.path);
|
||||
dialog.reset();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (dialog.is_selected(dialog::SOUND_OPEN))
|
||||
{
|
||||
auto add = [&]()
|
||||
{
|
||||
int id{};
|
||||
if (anm2.sound_add(document.directory_get().string(), dialog.path, id))
|
||||
{
|
||||
selection = {id};
|
||||
newSoundId = id;
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZED), std::make_format_args(id, dialog.path)));
|
||||
logger.info(std::vformat(localize.get(TOAST_SOUND_INITIALIZED, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, dialog.path)));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED), std::make_format_args(dialog.path)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(dialog.path)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_SOUND), Document::SOUNDS, add());
|
||||
|
||||
dialog.reset();
|
||||
}
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_ADD], shortcut::FOCUSED)) add_open();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_REMOVE], shortcut::FOCUSED)) remove_unused();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (imgui::shortcut(manager.chords[SHORTCUT_PASTE], shortcut::FOCUSED)) paste();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,23 @@ namespace anm2ed::imgui
|
||||
|
||||
auto center_view = [&]() { pan = -size * 0.5f; };
|
||||
|
||||
auto fit_view = [&]()
|
||||
{
|
||||
if (spritesheet && spritesheet->texture.is_valid())
|
||||
set_to_rect(zoom, pan, {0, 0, (float)spritesheet->texture.size.x, (float)spritesheet->texture.size.y});
|
||||
};
|
||||
|
||||
auto zoom_adjust = [&](float delta)
|
||||
{
|
||||
auto focus = position_translate(zoom, pan, size * 0.5f);
|
||||
auto previousZoom = zoom;
|
||||
zoom_set(zoom, pan, focus, delta);
|
||||
if (zoom != previousZoom) hasPendingZoomPanAdjust = true;
|
||||
};
|
||||
|
||||
auto zoom_in = [&]() { zoom_adjust(zoomStep); };
|
||||
auto zoom_out = [&]() { zoom_adjust(-zoomStep); };
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_SPRITESHEET_EDITOR_WINDOW), &settings.windowIsSpritesheetEditor))
|
||||
{
|
||||
|
||||
@@ -121,8 +138,7 @@ namespace anm2ed::imgui
|
||||
ImGui::SameLine();
|
||||
|
||||
imgui::shortcut(manager.chords[SHORTCUT_FIT]);
|
||||
if (ImGui::Button(localize.get(LABEL_FIT), widgetSize))
|
||||
if (spritesheet) set_to_rect(zoom, pan, {0, 0, spritesheet->texture.size.x, spritesheet->texture.size.y});
|
||||
if (ImGui::Button(localize.get(LABEL_FIT), widgetSize)) fit_view();
|
||||
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_FIT), settings.shortcutFit);
|
||||
|
||||
auto mousePosInt = ivec2(mousePos);
|
||||
@@ -425,6 +441,32 @@ namespace anm2ed::imgui
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupContextWindow("##Spritesheet Editor Context Menu", ImGuiMouseButton_Right))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(LABEL_CENTER_VIEW), settings.shortcutCenterView.c_str())) center_view();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(LABEL_FIT), settings.shortcutFit.c_str(), false,
|
||||
spritesheet && spritesheet->texture.is_valid()))
|
||||
fit_view();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ZOOM_IN), settings.shortcutZoomIn.c_str())) zoom_in();
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_ZOOM_OUT), settings.shortcutZoomOut.c_str())) zoom_out();
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (!document.isSpritesheetEditorSet)
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
|
||||
#include "log.h"
|
||||
#include "document.h"
|
||||
#include "filesystem_.h"
|
||||
#include "log.h"
|
||||
#include "strings.h"
|
||||
#include "toast.h"
|
||||
|
||||
@@ -24,70 +25,185 @@ namespace anm2ed::imgui
|
||||
auto& anm2 = document.anm2;
|
||||
auto& selection = document.spritesheet.selection;
|
||||
auto& unused = document.spritesheet.unused;
|
||||
auto& hovered = document.spritesheet.hovered;
|
||||
auto& reference = document.spritesheet.reference;
|
||||
auto style = ImGui::GetStyle();
|
||||
|
||||
hovered = -1;
|
||||
auto add_open = [&]() { dialog.file_open(dialog::SPRITESHEET_OPEN); };
|
||||
auto replace_open = [&]() { dialog.file_open(dialog::SPRITESHEET_REPLACE); };
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_SPRITESHEETS_WINDOW), &settings.windowIsSpritesheets))
|
||||
auto add = [&](const std::filesystem::path& path)
|
||||
{
|
||||
auto style = ImGui::GetStyle();
|
||||
if (path.empty()) return;
|
||||
document.spritesheet_add(path.string());
|
||||
newSpritesheetId = document.spritesheet.reference;
|
||||
};
|
||||
|
||||
auto context_menu = [&]()
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
if (unused.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto copy = [&]()
|
||||
for (auto& id : unused)
|
||||
{
|
||||
if (!selection.empty())
|
||||
{
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.spritesheets[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
}
|
||||
else if (hovered > -1)
|
||||
clipboard.set(anm2.content.spritesheets[hovered].to_string(hovered));
|
||||
};
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_REMOVE_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REMOVE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
anm2.content.spritesheets.erase(id);
|
||||
}
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
auto paste = [&](merge::Type type)
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SPRITESHEETS), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto reload = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
for (auto& id : selection)
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_SPRITESHEETS));
|
||||
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get().string(), type, &errorString))
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
spritesheet.reload(document.directory_get());
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_RELOAD_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_RELOAD_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_RELOAD_SPRITESHEETS), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto replace = [&](const std::filesystem::path& path)
|
||||
{
|
||||
if (selection.size() != 1 || path.empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
auto& id = *selection.begin();
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
spritesheet = anm2::Spritesheet(document.directory_get().string(), path);
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REPLACE_SPRITESHEET), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto save = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
for (auto& id : selection)
|
||||
{
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
auto pathString = spritesheet.path.string();
|
||||
if (spritesheet.save(document.directory_get().string()))
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED), std::make_format_args(id, pathString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto open_directory = [&](anm2::Spritesheet& spritesheet)
|
||||
{
|
||||
if (spritesheet.path.empty()) return;
|
||||
filesystem::WorkingDirectory workingDirectory(document.directory_get());
|
||||
dialog.file_explorer_open(spritesheet.path.parent_path());
|
||||
};
|
||||
|
||||
auto copy = [&]()
|
||||
{
|
||||
if (selection.empty()) return;
|
||||
|
||||
std::string clipboardText{};
|
||||
for (auto& id : selection)
|
||||
clipboardText += anm2.content.spritesheets[id].to_string(id);
|
||||
clipboard.set(clipboardText);
|
||||
};
|
||||
|
||||
auto paste = [&]()
|
||||
{
|
||||
if (clipboard.is_empty()) return;
|
||||
|
||||
auto behavior = [&]()
|
||||
{
|
||||
std::string errorString{};
|
||||
document.snapshot(localize.get(EDIT_PASTE_SPRITESHEETS));
|
||||
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get().string(), merge::APPEND,
|
||||
&errorString))
|
||||
document.change(Document::SPRITESHEETS);
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_DESERIALIZE_SPRITESHEETS_FAILED),
|
||||
std::make_format_args(errorString)));
|
||||
toasts.push(
|
||||
std::vformat(localize.get(TOAST_DESERIALIZE_SPRITESHEETS_FAILED), std::make_format_args(errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_DESERIALIZE_SPRITESHEETS_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(errorString)));
|
||||
}
|
||||
};
|
||||
|
||||
if (shortcut(manager.chords[SHORTCUT_COPY], shortcut::FOCUSED)) copy();
|
||||
if (shortcut(manager.chords[SHORTCUT_PASTE], 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::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, true);
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
|
||||
!selection.empty() || hovered > -1))
|
||||
copy();
|
||||
|
||||
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(BASIC_APPEND), settings.shortcutPaste.c_str())) paste(merge::APPEND);
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE))) paste(merge::REPLACE);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_SPRITESHEETS), Document::SPRITESHEETS, behavior());
|
||||
};
|
||||
|
||||
auto context_menu = [&]()
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow) &&
|
||||
ImGui::IsMouseClicked(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("##Spritesheet Context Menu");
|
||||
|
||||
if (ImGui::BeginPopup("##Spritesheet Context Menu"))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_OPEN_DIRECTORY), nullptr, false, selection.size() == 1))
|
||||
open_directory(anm2.content.spritesheets[*selection.begin()]);
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str())) add_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE_UNUSED), settings.shortcutRemove.c_str(), false, !unused.empty()))
|
||||
remove_unused();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_RELOAD), nullptr, false, !selection.empty())) reload();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REPLACE), nullptr, false, selection.size() == 1)) replace_open();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_SAVE), nullptr, false, !selection.empty())) save();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !selection.empty())) copy();
|
||||
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
|
||||
paste();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
};
|
||||
|
||||
if (ImGui::Begin(localize.get(LABEL_SPRITESHEETS_WINDOW), &settings.windowIsSpritesheets))
|
||||
{
|
||||
auto childSize = size_without_footer_get(2);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
@@ -97,7 +213,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
|
||||
|
||||
selection.start(anm2.content.spritesheets.size(), ImGuiMultiSelectFlags_ClearOnEscape);
|
||||
selection.start(anm2.content.spritesheets.size());
|
||||
|
||||
for (auto& [id, spritesheet] : anm2.content.spritesheets)
|
||||
{
|
||||
@@ -108,25 +224,17 @@ namespace anm2ed::imgui
|
||||
auto isSelected = selection.contains(id);
|
||||
auto isReferenced = id == reference;
|
||||
auto cursorPos = ImGui::GetCursorPos();
|
||||
bool isTextureValid = spritesheet.texture.is_valid();
|
||||
auto& texture = isTextureValid ? spritesheet.texture : resources.icons[icon::NONE];
|
||||
auto textureRef = ImTextureRef(texture.id);
|
||||
auto tintColor = !isTextureValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
bool isValid = spritesheet.texture.is_valid();
|
||||
auto& texture = isValid ? spritesheet.texture : resources.icons[icon::NONE];
|
||||
auto tintColor = !isValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
auto pathString = spritesheet.path.empty() ? std::string{anm2::NO_PATH} : spritesheet.path.string();
|
||||
auto pathCStr = pathString.c_str();
|
||||
|
||||
ImGui::SetNextItemSelectionUserData(id);
|
||||
ImGui::SetNextItemStorageID(id);
|
||||
if (ImGui::Selectable("##Spritesheet Selectable", isSelected, 0, spritesheetChildSize)) reference = id;
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
{
|
||||
filesystem::WorkingDirectory workingDirectory(document.directory_get().string());
|
||||
dialog.file_explorer_open(spritesheet.path.parent_path().string());
|
||||
}
|
||||
hovered = id;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
open_directory(spritesheet);
|
||||
if (newSpritesheetId == id)
|
||||
{
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
@@ -157,7 +265,7 @@ namespace anm2ed::imgui
|
||||
auto noScrollFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||
if (ImGui::BeginChild("##Spritesheet Tooltip Image Child", to_imvec2(textureSize), childFlags,
|
||||
noScrollFlags))
|
||||
ImGui::ImageWithBg(textureRef, to_imvec2(textureSize), ImVec2(), ImVec2(1, 1), ImVec4(), tintColor);
|
||||
ImGui::ImageWithBg(texture.id, to_imvec2(textureSize), ImVec2(), ImVec2(1, 1), ImVec4(), tintColor);
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
@@ -172,7 +280,7 @@ namespace anm2ed::imgui
|
||||
|
||||
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
|
||||
|
||||
if (!isTextureValid)
|
||||
if (!isValid)
|
||||
ImGui::TextUnformatted(localize.get(TOOLTIP_SPRITESHEET_INVALID));
|
||||
else
|
||||
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_TEXTURE_SIZE),
|
||||
@@ -196,7 +304,7 @@ namespace anm2ed::imgui
|
||||
imageSize.y = imageSize.x / aspectRatio;
|
||||
|
||||
ImGui::SetCursorPos(cursorPos);
|
||||
ImGui::ImageWithBg(textureRef, imageSize, ImVec2(), ImVec2(1, 1), ImVec4(), tintColor);
|
||||
ImGui::ImageWithBg(texture.id, imageSize, ImVec2(), ImVec2(1, 1), ImVec4(), tintColor);
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(spritesheetChildSize.y + style.ItemSpacing.x,
|
||||
@@ -206,8 +314,6 @@ namespace anm2ed::imgui
|
||||
ImGui::TextUnformatted(
|
||||
std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, pathCStr)).c_str());
|
||||
if (isReferenced) ImGui::PopFont();
|
||||
|
||||
context_menu();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
@@ -225,124 +331,47 @@ namespace anm2ed::imgui
|
||||
auto rowOneWidgetSize = widget_size_with_row_get(3);
|
||||
|
||||
shortcut(manager.chords[SHORTCUT_ADD]);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_OPEN);
|
||||
if (ImGui::Button(localize.get(BASIC_ADD), rowOneWidgetSize)) add_open();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_SPRITESHEET), settings.shortcutAdd);
|
||||
|
||||
if (dialog.is_selected(dialog::SPRITESHEET_OPEN))
|
||||
{
|
||||
document.spritesheet_add(dialog.path);
|
||||
newSpritesheetId = document.spritesheet.reference;
|
||||
add(dialog.path);
|
||||
dialog.reset();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.empty());
|
||||
{
|
||||
if (ImGui::Button(localize.get(BASIC_RELOAD), rowOneWidgetSize))
|
||||
{
|
||||
auto reload = [&]()
|
||||
{
|
||||
for (auto& id : selection)
|
||||
{
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
spritesheet.reload(document.directory_get());
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_RELOAD_SPRITESHEET),
|
||||
std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_RELOAD_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_RELOAD_SPRITESHEETS), Document::SPRITESHEETS, reload());
|
||||
}
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RELOAD_SPRITESHEETS));
|
||||
}
|
||||
if (ImGui::Button(localize.get(BASIC_RELOAD), rowOneWidgetSize)) reload();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RELOAD_SPRITESHEETS));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.size() != 1);
|
||||
{
|
||||
if (ImGui::Button(localize.get(BASIC_REPLACE), rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_REPLACE);
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPLACE_SPRITESHEET));
|
||||
}
|
||||
if (ImGui::Button(localize.get(BASIC_REPLACE), rowOneWidgetSize)) replace_open();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPLACE_SPRITESHEET));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (dialog.is_selected(dialog::SPRITESHEET_REPLACE))
|
||||
{
|
||||
auto replace = [&]()
|
||||
{
|
||||
auto& id = *selection.begin();
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
spritesheet = anm2::Spritesheet(document.directory_get().string(), dialog.path);
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET), std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REPLACE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REPLACE_SPRITESHEET), Document::SPRITESHEETS, replace());
|
||||
replace(dialog.path);
|
||||
dialog.reset();
|
||||
}
|
||||
|
||||
auto rowTwoWidgetSize = widget_size_with_row_get(2);
|
||||
|
||||
ImGui::BeginDisabled(unused.empty());
|
||||
{
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), rowTwoWidgetSize))
|
||||
{
|
||||
auto remove_unused = [&]()
|
||||
{
|
||||
for (auto& id : unused)
|
||||
{
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
auto pathString = spritesheet.path.string();
|
||||
toasts.push(std::vformat(localize.get(TOAST_REMOVE_SPRITESHEET),
|
||||
std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_REMOVE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
anm2.content.spritesheets.erase(id);
|
||||
}
|
||||
unused.clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SPRITESHEETS), Document::SPRITESHEETS,
|
||||
remove_unused());
|
||||
}
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_SPRITESHEETS), settings.shortcutRemove);
|
||||
}
|
||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), rowTwoWidgetSize)) remove_unused();
|
||||
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_SPRITESHEETS), settings.shortcutRemove);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(selection.empty());
|
||||
{
|
||||
if (ImGui::Button(localize.get(BASIC_SAVE), rowTwoWidgetSize))
|
||||
{
|
||||
for (auto& id : selection)
|
||||
{
|
||||
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
|
||||
auto pathString = spritesheet.path.string();
|
||||
if (spritesheet.save(document.directory_get().string()))
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET),
|
||||
std::make_format_args(id, pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
else
|
||||
{
|
||||
toasts.push(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED),
|
||||
std::make_format_args(id, pathString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_SAVE_SPRITESHEET_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(id, pathString)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::Button(localize.get(BASIC_SAVE), rowTwoWidgetSize)) save();
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SAVE_SPRITESHEETS));
|
||||
}
|
||||
|
||||
@@ -457,6 +457,7 @@ namespace anm2ed::imgui
|
||||
reference = {reference.animationIndex, type, id};
|
||||
frames_selection_reset();
|
||||
if (type == anm2::LAYER || type == anm2::NULL_) items.reference = (int)type;
|
||||
items.selection = {(int)id};
|
||||
};
|
||||
|
||||
auto fit_animation_length = [&]()
|
||||
@@ -689,6 +690,67 @@ namespace anm2ed::imgui
|
||||
item_selection_sync();
|
||||
};
|
||||
|
||||
auto item_context_menu = [&]()
|
||||
{
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
|
||||
|
||||
auto selectionType = item_selection_type_get();
|
||||
bool hasSelection = !itemSelection.empty() && (selectionType == anm2::LAYER || selectionType == anm2::NULL_);
|
||||
auto currentItem = document.item_get();
|
||||
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow) &&
|
||||
ImGui::IsMouseClicked(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("##Items Context Menu");
|
||||
|
||||
if (ImGui::BeginPopup("##Items Context Menu"))
|
||||
{
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_UNDO), settings.shortcutUndo.c_str(), false,
|
||||
document.is_able_to_undo()))
|
||||
document.undo();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(SHORTCUT_STRING_REDO), settings.shortcutRedo.c_str(), false,
|
||||
document.is_able_to_redo()))
|
||||
document.redo();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_ADD), settings.shortcutAdd.c_str(), false, animation))
|
||||
{
|
||||
item_properties_reset();
|
||||
propertiesPopup.open();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem(localize.get(BASIC_REMOVE), settings.shortcutRemove.c_str(), false,
|
||||
hasSelection || currentItem))
|
||||
{
|
||||
auto remove = [&]()
|
||||
{
|
||||
if (hasSelection)
|
||||
{
|
||||
std::vector<int> ids{};
|
||||
ids.reserve(itemSelection.size());
|
||||
for (auto value : itemSelection)
|
||||
ids.push_back(item_selection_decode(value));
|
||||
std::sort(ids.begin(), ids.end());
|
||||
for (auto id : ids)
|
||||
animation->item_remove(selectionType, id);
|
||||
item_selection_clear();
|
||||
}
|
||||
else if (reference.itemType == anm2::LAYER || reference.itemType == anm2::NULL_)
|
||||
animation->item_remove(reference.itemType, reference.itemID);
|
||||
reference_clear();
|
||||
};
|
||||
|
||||
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_ITEMS), Document::ITEMS, remove());
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
};
|
||||
|
||||
auto item_child = [&](anm2::Type type, int id, int& index)
|
||||
{
|
||||
ImGui::PushID(index);
|
||||
@@ -1118,6 +1180,8 @@ namespace anm2ed::imgui
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
item_context_menu();
|
||||
}
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::EndChild();
|
||||
@@ -1310,7 +1374,9 @@ namespace anm2ed::imgui
|
||||
{
|
||||
float frameTime{};
|
||||
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left) && !ImGui::IsAnyItemHovered())
|
||||
if (ImGui::IsWindowHovered() &&
|
||||
(ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsMouseReleased(ImGuiMouseButton_Right)) &&
|
||||
!ImGui::IsAnyItemHovered())
|
||||
reference_set_item(type, id);
|
||||
|
||||
for (int i = frameMin; i < frameMax; i++)
|
||||
@@ -1503,6 +1569,7 @@ namespace anm2ed::imgui
|
||||
if (type == anm2::TRIGGER || ImGui::IsKeyDown(ImGuiMod_Ctrl))
|
||||
{
|
||||
draggedFrame = &frame;
|
||||
draggedFrameType = type;
|
||||
draggedFrameIndex = (int)i;
|
||||
draggedFrameStart = hoveredTime;
|
||||
if (type != anm2::TRIGGER) draggedFrameStartDuration = draggedFrame->duration;
|
||||
@@ -1674,47 +1741,48 @@ namespace anm2ed::imgui
|
||||
frameSelectionSnapshot.assign(frames.selection.begin(), frames.selection.end());
|
||||
frameSelectionSnapshotReference = reference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (draggedFrame)
|
||||
if (draggedFrame)
|
||||
{
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
|
||||
if (!isDraggedFrameSnapshot && hoveredTime != draggedFrameStart)
|
||||
{
|
||||
isDraggedFrameSnapshot = true;
|
||||
document.snapshot(draggedFrameType == anm2::TRIGGER ? localize.get(EDIT_TRIGGER_AT_FRAME)
|
||||
: localize.get(EDIT_FRAME_DURATION));
|
||||
}
|
||||
|
||||
if (draggedFrameType == anm2::TRIGGER)
|
||||
{
|
||||
draggedFrame->atFrame =
|
||||
glm::clamp(hoveredTime, 0, settings.playbackIsClamp ? animation->frameNum - 1 : anm2::FRAME_NUM_MAX - 1);
|
||||
|
||||
for (auto [i, trigger] : std::views::enumerate(animation->triggers.frames))
|
||||
{
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
|
||||
if (!isDraggedFrameSnapshot && hoveredTime != draggedFrameStart)
|
||||
{
|
||||
isDraggedFrameSnapshot = true;
|
||||
document.snapshot(type == anm2::TRIGGER ? localize.get(EDIT_TRIGGER_AT_FRAME)
|
||||
: localize.get(EDIT_FRAME_DURATION));
|
||||
}
|
||||
|
||||
if (type == anm2::TRIGGER)
|
||||
{
|
||||
draggedFrame->atFrame = glm::clamp(
|
||||
hoveredTime, 0, settings.playbackIsClamp ? animation->frameNum - 1 : anm2::FRAME_NUM_MAX - 1);
|
||||
|
||||
for (auto [i, trigger] : std::views::enumerate(animation->triggers.frames))
|
||||
{
|
||||
if ((int)i == draggedFrameIndex) continue;
|
||||
if (trigger.atFrame == draggedFrame->atFrame) draggedFrame->atFrame--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
draggedFrame->duration = glm::clamp(draggedFrameStartDuration + (hoveredTime - draggedFrameStart),
|
||||
anm2::FRAME_DURATION_MIN, anm2::FRAME_DURATION_MAX);
|
||||
}
|
||||
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left))
|
||||
{
|
||||
document.change(Document::FRAMES);
|
||||
draggedFrame = nullptr;
|
||||
draggedFrameIndex = -1;
|
||||
draggedFrameStart = -1;
|
||||
draggedFrameStartDuration = -1;
|
||||
isDraggedFrameSnapshot = false;
|
||||
if (type == anm2::TRIGGER) item->frames_sort_by_at_frame();
|
||||
}
|
||||
if ((int)i == draggedFrameIndex) continue;
|
||||
if (trigger.atFrame == draggedFrame->atFrame) draggedFrame->atFrame--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
draggedFrame->duration = glm::clamp(draggedFrameStartDuration + (hoveredTime - draggedFrameStart),
|
||||
anm2::FRAME_DURATION_MIN, anm2::FRAME_DURATION_MAX);
|
||||
}
|
||||
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left))
|
||||
{
|
||||
document.change(Document::FRAMES);
|
||||
draggedFrame = nullptr;
|
||||
draggedFrameType = anm2::NONE;
|
||||
draggedFrameIndex = -1;
|
||||
draggedFrameStart = -1;
|
||||
draggedFrameStartDuration = -1;
|
||||
isDraggedFrameSnapshot = false;
|
||||
if (type == anm2::TRIGGER) item->frames_sort_by_at_frame();
|
||||
}
|
||||
}
|
||||
|
||||
context_menu();
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace anm2ed::imgui
|
||||
int addItemSpritesheetID{-1};
|
||||
int hoveredTime{};
|
||||
anm2::Frame* draggedFrame{};
|
||||
anm2::Type draggedFrameType{};
|
||||
int draggedFrameIndex{-1};
|
||||
int draggedFrameStart{-1};
|
||||
int draggedFrameStartDuration{-1};
|
||||
|
||||
@@ -21,6 +21,9 @@ using namespace anm2ed::util;
|
||||
|
||||
namespace anm2ed
|
||||
{
|
||||
constexpr auto WINDOW_4K_SIZE = glm::ivec2(3840, 2160);
|
||||
constexpr auto UI_SCALE_4K_DEFAULT = 1.5f;
|
||||
|
||||
constexpr auto SOCKET_ADDRESS = "127.0.0.1";
|
||||
constexpr auto SOCKET_PORT = 11414;
|
||||
|
||||
@@ -71,7 +74,7 @@ namespace anm2ed
|
||||
}
|
||||
}
|
||||
|
||||
std::string Loader::settings_path() { return filesystem::path_preferences_get() + "settings.ini"; }
|
||||
std::filesystem::path Loader::settings_path() { return filesystem::path_preferences_get() / "settings.ini"; }
|
||||
|
||||
Loader::Loader(int argc, const char** argv)
|
||||
{
|
||||
@@ -137,11 +140,11 @@ namespace anm2ed
|
||||
{
|
||||
if (auto mode = SDL_GetCurrentDisplayMode(display))
|
||||
{
|
||||
if (mode->w >= 3840 || mode->h >= 2160)
|
||||
settings.uiScale = 1.5f;
|
||||
else
|
||||
logger.warning(std::format("Failed to query primary display mode: {}", SDL_GetError()));
|
||||
if (mode->w >= WINDOW_4K_SIZE.x || mode->h >= WINDOW_4K_SIZE.y) settings.uiScale = UI_SCALE_4K_DEFAULT;
|
||||
SDL_SetWindowSize(window, mode->w - 1, mode->h - 1);
|
||||
}
|
||||
else
|
||||
logger.warning(std::format("Failed to query primary display mode: {}", SDL_GetError()));
|
||||
}
|
||||
else
|
||||
logger.warning("Failed to detect primary display for UI scaling.");
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace anm2ed
|
||||
{
|
||||
class Loader
|
||||
{
|
||||
std::string settings_path();
|
||||
std::filesystem::path settings_path();
|
||||
|
||||
public:
|
||||
Socket socket{};
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace anm2ed
|
||||
|
||||
Logger::Logger()
|
||||
{
|
||||
open(filesystem::path_preferences_get() + "log.txt");
|
||||
open(filesystem::path_preferences_get() / "log.txt");
|
||||
info("Initializing Anm2Ed");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,9 +57,8 @@ namespace anm2ed
|
||||
if (documents.empty()) selectionHistory.clear();
|
||||
}
|
||||
|
||||
std::filesystem::path Manager::recent_files_path_get() { return filesystem::path_preferences_get() + "recent.txt"; }
|
||||
std::filesystem::path Manager::autosave_path_get() { return filesystem::path_preferences_get() + "autosave.txt"; }
|
||||
std::filesystem::path Manager::autosave_directory_get() { return filesystem::path_preferences_get() + "autosave"; }
|
||||
std::filesystem::path Manager::recent_files_path_get() { return filesystem::path_preferences_get() / "recent.txt"; }
|
||||
std::filesystem::path Manager::autosave_path_get() { return filesystem::path_preferences_get() / "autosave.txt"; }
|
||||
|
||||
Manager::Manager()
|
||||
{
|
||||
@@ -79,8 +78,8 @@ namespace anm2ed
|
||||
if (!document.is_valid())
|
||||
{
|
||||
documents.pop_back();
|
||||
toasts.push(std::vformat(localize.get(TOAST_OPEN_DOCUMENT_FAILED),
|
||||
std::make_format_args(pathString, errorString)));
|
||||
toasts.push(
|
||||
std::vformat(localize.get(TOAST_OPEN_DOCUMENT_FAILED), std::make_format_args(pathString, errorString)));
|
||||
logger.error(std::vformat(localize.get(TOAST_OPEN_DOCUMENT_FAILED, anm2ed::ENGLISH),
|
||||
std::make_format_args(pathString, errorString)));
|
||||
return;
|
||||
@@ -92,8 +91,7 @@ namespace anm2ed
|
||||
pendingSelected = selected;
|
||||
selection_history_push(selected);
|
||||
toasts.push(std::vformat(localize.get(TOAST_OPEN_DOCUMENT), std::make_format_args(pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_OPEN_DOCUMENT, anm2ed::ENGLISH),
|
||||
std::make_format_args(pathString)));
|
||||
logger.info(std::vformat(localize.get(TOAST_OPEN_DOCUMENT, anm2ed::ENGLISH), std::make_format_args(pathString)));
|
||||
}
|
||||
|
||||
void Manager::new_(const std::filesystem::path& path) { open(path, true); }
|
||||
|
||||
@@ -91,7 +91,5 @@ namespace anm2ed
|
||||
void autosave_files_clear();
|
||||
|
||||
void chords_set(Settings&);
|
||||
|
||||
std::filesystem::path autosave_directory_get();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "audio.h"
|
||||
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
#include <SDL3/SDL_properties.h>
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <utility>
|
||||
|
||||
namespace anm2ed::resource
|
||||
@@ -11,20 +13,42 @@ namespace anm2ed::resource
|
||||
return mixer;
|
||||
}
|
||||
|
||||
Audio::Audio(const char* path)
|
||||
Audio::Audio(const std::filesystem::path& path)
|
||||
{
|
||||
if (path && *path) internal = MIX_LoadAudio(mixer_get(), path, true);
|
||||
if (path.empty()) return;
|
||||
|
||||
size_t fileSize = 0;
|
||||
void* fileData = SDL_LoadFile(path.string().c_str(), &fileSize);
|
||||
if (!fileData || fileSize == 0)
|
||||
{
|
||||
if (fileData) SDL_free(fileData);
|
||||
return;
|
||||
}
|
||||
|
||||
data.assign(static_cast<unsigned char*>(fileData), static_cast<unsigned char*>(fileData) + fileSize);
|
||||
SDL_free(fileData);
|
||||
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(data.data(), data.size());
|
||||
if (!io) data.clear();
|
||||
else
|
||||
{
|
||||
internal = MIX_LoadAudio_IO(mixer_get(), io, true, true);
|
||||
if (!internal) data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Audio::Audio(const std::string& path) : Audio(path.c_str()) {}
|
||||
|
||||
Audio::Audio(const std::filesystem::path& path) : Audio(path.string()) {}
|
||||
|
||||
Audio::Audio(const unsigned char* data, size_t size)
|
||||
Audio::Audio(const unsigned char* memory, size_t size)
|
||||
{
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(data, size);
|
||||
if (!io) return;
|
||||
internal = MIX_LoadAudio_IO(mixer_get(), io, true, true);
|
||||
if (!memory || size == 0) return;
|
||||
data.assign(memory, memory + size);
|
||||
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(data.data(), data.size());
|
||||
if (!io) data.clear();
|
||||
else
|
||||
{
|
||||
internal = MIX_LoadAudio_IO(mixer_get(), io, true, true);
|
||||
if (!internal) data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::unload()
|
||||
@@ -82,10 +106,25 @@ namespace anm2ed::resource
|
||||
|
||||
bool Audio::is_playing() const { return track && MIX_TrackPlaying(track); }
|
||||
|
||||
Audio::Audio(const Audio& other)
|
||||
{
|
||||
if (other.data.empty()) return;
|
||||
|
||||
data = other.data;
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(data.data(), data.size());
|
||||
if (!io) data.clear();
|
||||
else
|
||||
{
|
||||
internal = MIX_LoadAudio_IO(mixer_get(), io, true, true);
|
||||
if (!internal) data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Audio::Audio(Audio&& other) noexcept
|
||||
{
|
||||
internal = std::exchange(other.internal, nullptr);
|
||||
track = std::exchange(other.track, nullptr);
|
||||
data = std::move(other.data);
|
||||
}
|
||||
|
||||
Audio& Audio::operator=(Audio&& other) noexcept
|
||||
@@ -95,6 +134,28 @@ namespace anm2ed::resource
|
||||
unload();
|
||||
internal = std::exchange(other.internal, nullptr);
|
||||
track = std::exchange(other.track, nullptr);
|
||||
data = std::move(other.data);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Audio& Audio::operator=(const Audio& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
unload();
|
||||
data.clear();
|
||||
if (!other.data.empty())
|
||||
{
|
||||
data = other.data;
|
||||
SDL_IOStream* io = SDL_IOFromConstMem(data.data(), data.size());
|
||||
if (!io) data.clear();
|
||||
else
|
||||
{
|
||||
internal = MIX_LoadAudio_IO(mixer_get(), io, true, true);
|
||||
if (!internal) data.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace anm2ed::resource
|
||||
{
|
||||
@@ -11,20 +11,19 @@ namespace anm2ed::resource
|
||||
{
|
||||
MIX_Audio* internal{nullptr};
|
||||
MIX_Track* track{nullptr};
|
||||
std::vector<unsigned char> data{};
|
||||
MIX_Mixer* mixer_get();
|
||||
void unload();
|
||||
|
||||
public:
|
||||
Audio(const char*);
|
||||
Audio(const unsigned char*, size_t);
|
||||
Audio(const std::string&);
|
||||
Audio(const std::filesystem::path&);
|
||||
~Audio();
|
||||
Audio() = default;
|
||||
Audio(const Audio&);
|
||||
Audio(Audio&&) noexcept;
|
||||
Audio& operator=(const Audio&);
|
||||
Audio& operator=(Audio&&) noexcept;
|
||||
Audio(const Audio&) = delete;
|
||||
Audio& operator=(const Audio&) = delete;
|
||||
|
||||
bool is_valid();
|
||||
void play(bool loop = false, MIX_Mixer* = nullptr);
|
||||
|
||||
@@ -153,6 +153,10 @@ namespace anm2ed::resource::icon
|
||||
<svg viewBox="0 0 24 48" fill="#FFF" xmlns="http://www.w3.org/2000/svg"> <path d="M4 0H20V38L12 48L4 38V0Z"/> </svg>
|
||||
)";
|
||||
|
||||
inline constexpr auto SOUND_DATA = R"(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FFF"><path d="M6.60282 10.0001L10 7.22056V16.7796L6.60282 14.0001H3V10.0001H6.60282ZM2 16.0001H5.88889L11.1834 20.3319C11.2727 20.405 11.3846 20.4449 11.5 20.4449C11.7761 20.4449 12 20.2211 12 19.9449V4.05519C12 3.93977 11.9601 3.8279 11.887 3.73857C11.7121 3.52485 11.3971 3.49335 11.1834 3.66821L5.88889 8.00007H2C1.44772 8.00007 1 8.44778 1 9.00007V15.0001C1 15.5524 1.44772 16.0001 2 16.0001ZM23 12C23 15.292 21.5539 18.2463 19.2622 20.2622L17.8445 18.8444C19.7758 17.1937 21 14.7398 21 12C21 9.26016 19.7758 6.80629 17.8445 5.15557L19.2622 3.73779C21.5539 5.75368 23 8.70795 23 12ZM18 12C18 10.0883 17.106 8.38548 15.7133 7.28673L14.2842 8.71584C15.3213 9.43855 16 10.64 16 12C16 13.36 15.3213 14.5614 14.2842 15.2841L15.7133 16.7132C17.106 15.6145 18 13.9116 18 12Z"></path></svg>
|
||||
)";
|
||||
|
||||
#define SVG_LIST \
|
||||
X(NONE, NONE_DATA, SIZE_SMALL) \
|
||||
X(FILE, FILE_DATA, SIZE_NORMAL) \
|
||||
@@ -161,6 +165,7 @@ namespace anm2ed::resource::icon
|
||||
X(ROOT, ROOT_DATA, SIZE_NORMAL) \
|
||||
X(LAYER, LAYER_DATA, SIZE_NORMAL) \
|
||||
X(NULL_, NULL_DATA, SIZE_NORMAL) \
|
||||
X(SOUND, SOUND_DATA, SIZE_NORMAL) \
|
||||
X(TRIGGERS, TRIGGERS_DATA, SIZE_NORMAL) \
|
||||
X(VISIBLE, VISIBLE_DATA, SIZE_NORMAL) \
|
||||
X(INVISIBLE, INVISIBLE_DATA, SIZE_NORMAL) \
|
||||
|
||||
@@ -66,9 +66,12 @@ namespace anm2ed
|
||||
X(BASIC_NULL_ANIMATION, "Null", "Null", "Нуль", "无", "Null") \
|
||||
X(BASIC_OFFSET, "Offset", "Offset", "Смещение", "偏移", "오프셋") \
|
||||
X(BASIC_OPEN, "Open", "Abrir", "Открыть", "打开", "열기") \
|
||||
X(BASIC_OPEN_DIRECTORY, "Open Directory", "Abrir Directorio", "Открыть директорию", "打开目录", "디렉터리 열기") \
|
||||
X(BASIC_PASTE, "Paste", "Pegar", "Вставить", "粘贴", "붙여넣기") \
|
||||
X(BASIC_PIVOT, "Pivot", "Pivote", "Точка вращения", "枢轴", "중심점") \
|
||||
X(BASIC_POSITION, "Position", "Posicion", "Позиция", "位置", "위치") \
|
||||
X(BASIC_PROPERTIES, "Properties", "Propiedades", "Свойства", "属性", "속성") \
|
||||
X(BASIC_RENAME, "Rename", "Renombrar", "Переименовать", "重命名", "이름 변경") \
|
||||
X(BASIC_RELOAD, "Reload", "Recargar", "Перезагрузить", "重新加载", "다시 불러오기") \
|
||||
X(BASIC_REMOVE, "Remove", "Remover", "Удалить", "删除", "제거") \
|
||||
X(BASIC_REMOVE_UNUSED, "Remove Unused", "Remover no utilizados", "Удалить неизпользуемые", "删除未使用", "미사용 시트 제거") \
|
||||
@@ -132,8 +135,10 @@ namespace anm2ed
|
||||
X(EDIT_PASTE_FRAMES, "Paste Frame(s)", "Pegar Frames", "Вставить кадры", "粘贴帧", "프레임 붙여넣기") \
|
||||
X(EDIT_PASTE_LAYERS, "Paste Layer(s)", "Pegar Capa(s)", "Вставить слои", "粘贴动画层", "레이어 붙여넣기") \
|
||||
X(EDIT_PASTE_NULLS, "Paste Null(s)", "Pegar Null(s)", "Вставить нули", "粘贴Null", "Null 붙여넣기") \
|
||||
X(EDIT_PASTE_SOUNDS, "Paste Sound(s)", "Pegar Sonido(s)", "Вставить звук(и)", "粘贴声音", "사운드 붙여넣기") \
|
||||
X(EDIT_PASTE_SPRITESHEETS, "Paste Spritesheet(s)", "Pegar Spritesheet(s)", "Вставить спрайт-листы", "粘贴图集", "스프라이트 시트 붙여넣기") \
|
||||
X(EDIT_RELOAD_SPRITESHEETS, "Reload Spritesheet(s)", "Recargar Spritesheet(s)", "Перезагрузить спрайт-листы", "重新加载图集", "스프라이트 시트 다시 불러오기") \
|
||||
X(EDIT_RELOAD_SOUNDS, "Reload Sound(s)", "Recargar Sonido(s)", "Перезагрузить звук(и)", "重新加载声音", "사운드 다시 불러오기") \
|
||||
X(EDIT_REMOVE_ANIMATIONS, "Remove Animation(s)", "Remover Animacion(es)", "Удалить анимации", "删除动画层", "애니메이션 제거") \
|
||||
X(EDIT_REMOVE_ITEMS, "Remove Item(s)", "Remover Item(s)", "Удалить предметы", "删除物品", "항목 제거") \
|
||||
X(EDIT_REMOVE_UNUSED_EVENTS, "Remove Unused Events", "Remover Eventos No Utilizados", "Удалить неизпользуемые события", "删除未使用的事件", "미사용 이벤트 제거") \
|
||||
@@ -143,6 +148,7 @@ namespace anm2ed
|
||||
X(EDIT_REMOVE_UNUSED_SPRITESHEETS, "Remove Unused Spritesheets", "Remover Spritesheets No Utilizadas", "Удалить неизпользуемые спрайт-листы", "删除未使用的图集", "미사용 스프라이트 시트 제거") \
|
||||
X(EDIT_RENAME_EVENT, "Rename Event", "Renombrar Evento", "Переименовать событие", "重命名事件", "이벤트 이름 바꾸기") \
|
||||
X(EDIT_REPLACE_SPRITESHEET, "Replace Spritesheet", "Reemplazar Spritesheet", "Заменить спрайт-лист", "替换图集", "스프라이트 시트 교체") \
|
||||
X(EDIT_REPLACE_SOUND, "Replace Sound", "Reemplazar Sonido", "Заменить звук", "替换声音", "사운드 교체") \
|
||||
X(EDIT_SET_LAYER_PROPERTIES, "Set Layer Properties", "Establecer Propiedades de Capa", "Установить свойства слоя", "更改动画层属性", "레이어 속성 설정") \
|
||||
X(EDIT_SET_NULL_PROPERTIES, "Set Null Properties", "Establecer Propiedades Null", "Установить свойства нуля", "更改Null属性", "Null 속성 설정") \
|
||||
X(EDIT_SPLIT_FRAME, "Split Frame", "Dividir Frame", "Разделить кадр", "拆分帧", "프레임 분할") \
|
||||
@@ -175,6 +181,7 @@ namespace anm2ed
|
||||
X(FORMAT_SIZE, "Size: ({0}, {1})", "Tamaño: ({0}, {1})", "Размер: ({0}, {1})", "大小: ({0}, {1})", "크기: ({0}, {1})") \
|
||||
X(FORMAT_SOUND_LABEL, "Sound: {0}", "Sonido: {0}", "Звук: {0}", "声音: {0}", "사운드: {0}") \
|
||||
X(FORMAT_SPRITESHEET, "#{0} {1}", "#{0} {1}", "#{0} {1}", "#{0} {1}", "#{0} {1}") \
|
||||
X(FORMAT_SOUND, "#{0} {1}", "#{0} {1}", "#{0} {1}", "#{0} {1}", "#{0} {1}") \
|
||||
X(FORMAT_TRANSFORM, "Transform: {0}", "Transformar: {0}", "Трансформация: {0}", "变换: {0}", "변환: {0}") \
|
||||
X(FORMAT_RECT, "Rect: {0}", "Rect: {0}", "Прямоугольник: {0}", "矩形: {0}", "사각형: {0}") \
|
||||
X(FORMAT_FRAMES_COUNT, "Frames: {0}", "Frames: {0}", "Кадры: {0}", "帧: {0}", "프레임: {0}") \
|
||||
@@ -246,7 +253,6 @@ namespace anm2ed
|
||||
X(LABEL_LAYER, "Layer", "Capa", "Слой", "动画层", "레이어") \
|
||||
X(LABEL_LAYERS_CHILD, "Layers List", "Lista de Capas", "", "动画层列表", "레이어 목록") \
|
||||
X(LABEL_LAYERS_WINDOW, "Layers###Layers", "Capas###Layers", "Слои###Layers", "动画层###Layers", "레이어###Layers") \
|
||||
X(LABEL_LENGTH, "Length", "Duracion", "", "长度", "길이") \
|
||||
X(LABEL_THIS_ANIMATION, "This Animation", "Esta Animacion", "Эта анимация", "此动画", "이 애니메이션") \
|
||||
X(LABEL_DESTINATION, "Destination", "Destino", "Назначение", "目标", "대상") \
|
||||
X(LABEL_LOCALIZATION, "Localization", "Localizacion", "Локализация", "本地化", "현지화") \
|
||||
@@ -361,6 +367,7 @@ namespace anm2ed
|
||||
X(SHORTCUT_STRING_PLAY_PAUSE, "Play/Pause", "Reproducir/Pausar", "Возпроизвести/Пауза", "播放/暂停", "재생/일시정지") \
|
||||
X(SHORTCUT_STRING_PREVIOUS_FRAME, "Previous Frame", "Frame Anterior", "Предыдущий кадр", "前一帧", "이전 프레임") \
|
||||
X(SHORTCUT_STRING_REDO, "Redo", "Rehacer", "Повторить", "重做", "다시 실행") \
|
||||
X(SHORTCUT_STRING_RENAME, "Rename", "Renombrar", "Переименовать", "重命名", "이름 변경") \
|
||||
X(SHORTCUT_STRING_REMOVE, "Remove", "Remover", "Удалить", "去除", "제거") \
|
||||
X(SHORTCUT_STRING_ROTATE, "Rotate", "Rotar", "Поворачивать", "旋转", "회전") \
|
||||
X(SHORTCUT_STRING_SAVE, "Save", "Guardar", "Сохранить", "保存", "저장") \
|
||||
@@ -404,8 +411,10 @@ namespace anm2ed
|
||||
X(TOAST_PNG_DIRECTORY_NOT_SET, "PNG output directory must be set.", "El directorio de salida de PNG debe ser ajustado.", "Директория для вывода PNG должна быть установлена.", "必须设置PNG输出目录.", "PNG를 출력할 경로를 설정해야 합니다.") \
|
||||
X(TOAST_REDO, "Redo: {0}", "Rehacer: {0}", "Повтор: {0}", "重做: {0}", "다시 실행: {0}") \
|
||||
X(TOAST_RELOAD_SPRITESHEET, "Reloaded spritesheet #{0}: {1}", "Se ha recargado spritesheet #{0}: {1}", "Спрайт-лист #{0} перезагружен: {1}", "重新加载了图集 #{0}: {1}", "{0}번 스프라이트 시트 다시 불러옴: {1}") \
|
||||
X(TOAST_RELOAD_SOUND, "Reloaded sound #{0}: {1}", "Se ha recargado sonido #{0}: {1}", "Звук #{0} перезагружен: {1}", "重新加载了声音 #{0}: {1}", "{0}번 사운드 다시 불러옴: {1}") \
|
||||
X(TOAST_REMOVE_SPRITESHEET, "Removed spritesheet #{0}: {1}", "Se ha removido spritesheet #{0}: {1}", "Спрайт-лист #{0} удален: {1}", "去除了图集 #{0}: {1}", "{0}번 스프라이트 시트 제거됨: {1}") \
|
||||
X(TOAST_REPLACE_SPRITESHEET, "Replaced spritesheet #{0}: {1}", "Se ha reemplazado spritesheet #{0}: {1}", "Спрайт-лист #{0} заменен: {1}", "替换了图集 #{0}: {1}", "{0}번 스프라이트 시트 교체됨: {1}") \
|
||||
X(TOAST_REPLACE_SOUND, "Replaced sound #{0}: {1}", "Se ha reemplazado sonido #{0}: {1}", "Звук #{0} заменен: {1}", "已替换声音 #{0}: {1}", "{0}번 사운드 교체됨: {1}") \
|
||||
X(TOAST_SAVE_DOCUMENT, "Saved document to: {0}", "Documento Guardado en: {0}", "Документ сохранен в: {0}", "保存文件至: {0}", "{0}에 파일을 저장했습니다.") \
|
||||
X(TOAST_SAVE_DOCUMENT_FAILED, "Could not save document to: {0} ({1})", "No se pudo guardar el documento en: {0} ({1})", "Не удалось сохранить документ в: {0} ({1})", "无法保存文件至: {0} ({1})", "{0}에 파일을 저장할 수 없습니다.") \
|
||||
X(TOAST_SAVE_SPRITESHEET, "Saved spritesheet #{0}: {1}", "Spritesheet Guardada #{0}: {1}", "Спрайт-лист #{0} сохранен: {1}", "已保存图集 #{0}: {1}", "{1}에 {0}번 스프라이트 시트를 저장했습니다.") \
|
||||
@@ -509,6 +518,7 @@ namespace anm2ed
|
||||
X(TOOLTIP_PREVIEW_ZOOM, "Change the zoom of the preview.", "Cambia el zoom de la vista previa.", "Изменить масштаб предпросмотра.", "更改预览视图的缩放.", "미리보기의 줌을 변경합니다.") \
|
||||
X(TOOLTIP_RAW, "Record only the raw animation; i.e., only its layers, to its bounds.", "Graba solo la animacion cruda; i. e., Solo sus capas.", "Записывать только «сырую» анимацию, т. е. только ее слои.", "仅保存原生动画; 比如只保存动画层.", "Raw 애니메이션만 녹화합니다. 즉, 레이어만 녹화합니다.") \
|
||||
X(TOOLTIP_RELOAD_SPRITESHEETS, "Reloads the selected spritesheets.", "Recarga la spritesheet seleccionada.", "Перезагружает выбранные спрайт-листы.", "重新加载所选图集.", "선택한 스프라이트 시트를 다시 불러옵니다.") \
|
||||
X(TOOLTIP_RELOAD_SOUNDS, "Reloads the selected sounds.", "Recarga los sonidos seleccionados.", "Перезагружает выбранные звуки.", "重新加载所选声音.", "선택한 사운드를 다시 불러옵니다.") \
|
||||
X(TOOLTIP_REMOVE_ANIMATION, "Remove the selected animation(s).", "Remueve la(s) animacion(es) seleccionada(s).", "Удалить выбранные анимации.", "去除所选动画.", "선택한 애니메이션을 제거합니다.") \
|
||||
X(TOOLTIP_REMOVE_ITEMS, "Remove the selected item(s).", "Remueve el/los item(s) seleccionado(s).", "Удалить выбранные предметы.", "去除所选物品.", "선택한 항목을 제거합니다.") \
|
||||
X(TOOLTIP_REMOVE_UNUSED_EVENTS, "Remove unused events (i.e., ones not used by any trigger in any animation.)", "Remueve eventos no utilizados (i. e., aquellos no usados por algun trigger en ninguna animacion.)", "Удалить неиспользуемые события (т. е. события, которые не использует ни один триггер в ни одной анимации.)", "去除未使用的事件 (未被任何动画触发的事件.)", "사용되지 않는 이벤트(어떤 애니메이션의 트리거에서도 사용되지 않는 것)를 제거합니다.") \
|
||||
@@ -520,6 +530,7 @@ namespace anm2ed
|
||||
X(TOOLTIP_RENDER_TYPE, "Set the type of the output.", "Ajusta el tipo de la salida", "Установить тип вывода.", "设置输出的类型.", "출력 유형을 설정합니다.") \
|
||||
X(TOOLTIP_REPEAT_DELAY, "Set how often, after repeating begins, key inputs will be fired.", "Ajusta que tanto, despues de que empieza la repeticion, seran disparadas las entradas clave.", "Установить, как часто после начала повторения будут срабатывать нажатия клавиш.", "更改键盘按键开启重复时, 重复的速率.", "반복 입력이 시작된 후 입력이 얼마나 자주 들어갈지 설정합니다.") \
|
||||
X(TOOLTIP_REPLACE_SPRITESHEET, "Replace the selected spritesheet with a new one.", "Reemplaza la spritesheet seleccionada con una nueva.", "Заменить выбранный спрайт-лист на новый.", "替换所选旧图集为新图集.", "선택된 스프라이트 시트를 새 시트로 교체합니다.") \
|
||||
X(TOOLTIP_REPLACE_SOUND, "Replace the selected sound with a new one.", "Reemplaza el sonido seleccionado con uno nuevo.", "Заменяет выбранный звук новым.", "用新的声音替换所选声音.", "선택한 사운드를 새 사운드로 교체합니다.") \
|
||||
X(TOOLTIP_ROOT_TRANSFORM, "Root frames will transform the rest of the animation.", "Los Frames root transformaran el resto de la animacion.", "Корневые кадры трансформируют остаток анимации.", "与“根”有关的帧会跟随整个动画而变换.", "Root 프레임이 나머지 애니메이션을 변형합니다.") \
|
||||
X(TOOLTIP_ROTATION, "Change the rotation of the frame.", "Cambia la rotacion del Frame.", "Изменить поворот кадра.", "更改此帧的旋转.", "프레임의 회전값을 변경합니다.") \
|
||||
X(TOOLTIP_ROUND_ROTATION, "Rotation will be rounded to the nearest whole number.", "La rotacion sera aproximada al numero entero mas cercano.", "Поворот будет округлен к самому близкому целому числу.", "旋转数值会被取整.", "회전값을 가장 가까운 정수로 반올림합니다.") \
|
||||
@@ -534,7 +545,7 @@ namespace anm2ed
|
||||
X(TOOLTIP_SPLIT, "Based on the playhead time, split the selected frame into two.", "Basado en tiempo del encabezado de reproduccion, divide el Frame seleccionado en dos.", "С учётом позиции ползунка воспроизведения разделяет выбранный кадр на два.", "根据播放头位置,将所选帧拆分成两个。", "재생 헤드 시간에 따라 선택한 프레임을 두 개로 분할합니다.") \
|
||||
X(TOOLTIP_SIZE, "Change the crop size the frame uses.", "Cambia el tamaño de recorte que usa el Frame.", "Изменить размер обрезки, который использует этот кадр.", "更改此帧的裁剪大小.", "프레임에 대응되는 스프라이트 시트의 사용 영역의 크기를 변경합니다.") \
|
||||
X(TOOLTIP_SOUND, "Toggle sounds playing with triggers.\nBind sounds to events in the Events window.", "Alterna los sonidos reproduciendoce con triggers.\nEnlaza sonidos a eventos en la Ventana de Eventos.", "Переключить воспроизведения звуков с помощью триггеров.\nПривязывайте звуки к событиям в окне событий.", "切换是否在触发器触发时播放声音.\n可以在事件窗口里链接声音与事件.", "트리거와 함께 사운드를 재생할지 정합니다.\n사운드는 이벤트 창에서 이벤트에 연결하세요.") \
|
||||
X(TOOLTIP_SOUNDS_PLAY, "Click to play.", "Click para reproducir.", "Нажмите, чтобы возпроизвести.", "点击播放.", "클릭하여 재생합니다.") \
|
||||
X(TEXT_SOUND_PLAY, "Click to play.", "Click para reproducir.", "Нажмите, чтобы возпроизвести.", "点击播放.", "클릭하여 재생합니다.") \
|
||||
X(TOOLTIP_SOUND_INVALID, "This sound could not be loaded. Replace the file.", "Este sonido no se pudo cargar. Reemplaza el archivo.", "Этот звук не удалось загрузить. Замените файл.", "无法加载此声音。请替换文件。", "이 사운드를 불러올 수 없습니다. 파일을 교체하세요.") \
|
||||
X(TOOLTIP_SOUND_ADD, "Add a sound.", "Añadir un sonido.", "Добавить звук.", "添加一个声音.", "사운드를 추가합니다.") \
|
||||
X(TOOLTIP_SPRITESHEET_BORDER, "Toggle a border appearing around the spritesheet.", "Alterna un borde apareciendo alrededor del spritesheet", "Переключить показ границ около спрайт-листа.", "切换是否显示在图集的边框.", "스프라이트 시트 주변에 경계선을 표시하거나 숨깁니다.") \
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace anm2ed::resource
|
||||
|
||||
Texture::Texture(Texture&& other) { *this = std::move(other); }
|
||||
|
||||
Texture& Texture::operator=(const Texture& other) // Copy
|
||||
Texture& Texture::operator=(const Texture& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace anm2ed::resource
|
||||
return *this;
|
||||
}
|
||||
|
||||
Texture& Texture::operator=(Texture&& other) // Move
|
||||
Texture& Texture::operator=(Texture&& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
@@ -114,7 +114,7 @@ namespace anm2ed::resource
|
||||
upload(data);
|
||||
}
|
||||
|
||||
Texture::Texture(const std::string& pngPath)
|
||||
Texture::Texture(const std::filesystem::path& pngPath)
|
||||
{
|
||||
if (const uint8_t* data = stbi_load(pngPath.c_str(), &size.x, &size.y, nullptr, CHANNELS); data)
|
||||
{
|
||||
@@ -123,15 +123,11 @@ namespace anm2ed::resource
|
||||
}
|
||||
}
|
||||
|
||||
Texture::Texture(const std::filesystem::path& pngPath) : Texture(pngPath.string()) {}
|
||||
|
||||
bool Texture::write_png(const std::string& path)
|
||||
bool Texture::write_png(const std::filesystem::path& path)
|
||||
{
|
||||
return stbi_write_png(path.c_str(), size.x, size.y, CHANNELS, this->pixels.data(), size.x * CHANNELS);
|
||||
}
|
||||
|
||||
bool Texture::write_png(const std::filesystem::path& path) { return write_png(path.string()); }
|
||||
|
||||
vec4 Texture::pixel_read(vec2 position) const
|
||||
{
|
||||
if (pixels.size() < CHANNELS || size.x <= 0 || size.y <= 0) return vec4(0.0f);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
@@ -37,9 +36,7 @@ namespace anm2ed::resource
|
||||
Texture& operator=(Texture&&);
|
||||
Texture(const uint8_t*, glm::ivec2);
|
||||
Texture(const char*, size_t, glm::ivec2);
|
||||
Texture(const std::string&);
|
||||
Texture(const std::filesystem::path&);
|
||||
bool write_png(const std::string&);
|
||||
bool write_png(const std::filesystem::path&);
|
||||
static bool write_pixels_png(const std::filesystem::path&, glm::ivec2, const uint8_t*);
|
||||
void pixel_set(glm::ivec2, glm::vec4);
|
||||
|
||||
@@ -119,10 +119,10 @@ DockSpace ID=0x123F8F08 Window=0x6D581B32 Pos=8,62 Size=1902,994 Spl
|
||||
DockNode ID=0x00000006 Parent=0x123F8F08 SizeRef=1910,268 Selected=0x597925B7
|
||||
)";
|
||||
|
||||
Settings::Settings(const std::string& path)
|
||||
Settings::Settings(const std::filesystem::path& path)
|
||||
{
|
||||
if (filesystem::path_is_exist(path))
|
||||
logger.info(std::format("Using settings from: {}", path));
|
||||
logger.info(std::format("Using settings from: {}", path.string()));
|
||||
else
|
||||
{
|
||||
logger.warning("Settings file does not exist; using default");
|
||||
@@ -133,7 +133,7 @@ DockSpace ID=0x123F8F08 Window=0x6D581B32 Pos=8,62 Size=1902,994 Spl
|
||||
std::ifstream file(path);
|
||||
if (!file.is_open())
|
||||
{
|
||||
logger.error(std::format("Failed to open settings file: {}", path));
|
||||
logger.error(std::format("Failed to open settings file: {}", path.string()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ DockSpace ID=0x123F8F08 Window=0x6D581B32 Pos=8,62 Size=1902,994 Spl
|
||||
file.close();
|
||||
}
|
||||
|
||||
void Settings::save(const std::string& path, const std::string& imguiData)
|
||||
void Settings::save(const std::filesystem::path& path, const std::string& imguiData)
|
||||
{
|
||||
std::ofstream file(path, std::ios::out | std::ios::binary);
|
||||
file << "[Settings]\n";
|
||||
|
||||
@@ -17,6 +17,12 @@ namespace anm2ed
|
||||
constexpr auto FFMPEG_PATH_DEFAULT = "/usr/bin/ffmpeg";
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr auto OUTPUT_PATH_DEFAULT = ".\\output.gif";
|
||||
#else
|
||||
constexpr auto OUTPUT_PATH_DEFAULT = "./output.gif";
|
||||
#endif
|
||||
|
||||
#define SETTINGS_TYPES \
|
||||
X(INT, int) \
|
||||
X(BOOL, bool) \
|
||||
@@ -42,7 +48,7 @@ namespace anm2ed
|
||||
|
||||
#define SETTINGS_MEMBERS \
|
||||
/* Symbol / Name / String / Type / Default */ \
|
||||
X(WINDOW_SIZE, windowSize, STRING_UNDEFINED, IVEC2_WH, {1600, 900}) \
|
||||
X(WINDOW_SIZE, windowSize, STRING_UNDEFINED, IVEC2_WH, {1200, 720}) \
|
||||
X(IS_VSYNC, isVsync, STRING_UNDEFINED, BOOL, true) \
|
||||
X(UI_SCALE, uiScale, STRING_UNDEFINED, FLOAT, 1.0f) \
|
||||
X(THEME, theme, STRING_UNDEFINED, INT, types::theme::DARK) \
|
||||
@@ -150,7 +156,7 @@ namespace anm2ed
|
||||
X(TOOL_COLOR, toolColor, STRING_UNDEFINED, VEC4, {1.0, 1.0, 1.0, 1.0}) \
|
||||
\
|
||||
X(RENDER_TYPE, renderType, STRING_UNDEFINED, INT, render::GIF) \
|
||||
X(RENDER_PATH, renderPath, STRING_UNDEFINED, STRING, "./output.gif") \
|
||||
X(RENDER_PATH, renderPath, STRING_UNDEFINED, STRING, OUTPUT_PATH_DEFAULT) \
|
||||
X(RENDER_ROWS, renderRows, STRING_UNDEFINED, INT, 0) \
|
||||
X(RENDER_COLUMNS, renderColumns, STRING_UNDEFINED, INT, 0) \
|
||||
X(RENDER_FORMAT, renderFormat, STRING_UNDEFINED, STRING, "{}.png") \
|
||||
@@ -176,6 +182,7 @@ namespace anm2ed
|
||||
X(SHORTCUT_DUPLICATE, shortcutDuplicate, SHORTCUT_STRING_DUPLICATE, STRING, "Ctrl+J") \
|
||||
X(SHORTCUT_ADD, shortcutAdd, SHORTCUT_STRING_ADD, STRING, "Insert") \
|
||||
X(SHORTCUT_REMOVE, shortcutRemove, SHORTCUT_STRING_REMOVE, STRING, "Delete") \
|
||||
X(SHORTCUT_RENAME, shortcutRename, SHORTCUT_STRING_RENAME, STRING, "F2") \
|
||||
X(SHORTCUT_DEFAULT, shortcutDefault, SHORTCUT_STRING_DEFAULT, STRING, "Home") \
|
||||
X(SHORTCUT_MERGE, shortcutMerge, SHORTCUT_STRING_MERGE, STRING, "Ctrl+E") \
|
||||
/* Tools */ \
|
||||
@@ -241,8 +248,8 @@ namespace anm2ed
|
||||
|
||||
Settings() = default;
|
||||
|
||||
Settings(const std::string&);
|
||||
void save(const std::string&, const std::string&);
|
||||
Settings(const std::filesystem::path&);
|
||||
void save(const std::filesystem::path&, const std::string&);
|
||||
};
|
||||
|
||||
constexpr StringType SHORTCUT_STRING_TYPES[] = {
|
||||
|
||||
@@ -2,48 +2,76 @@
|
||||
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <cctype>
|
||||
#include <cwctype>
|
||||
#include <filesystem>
|
||||
|
||||
#include "string_.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace anm2ed::util::filesystem
|
||||
{
|
||||
std::string path_preferences_get()
|
||||
namespace
|
||||
{
|
||||
auto path = SDL_GetPrefPath(nullptr, "anm2ed");
|
||||
std::string string = path;
|
||||
SDL_free(path);
|
||||
return string;
|
||||
template <typename CharT> CharT to_lower_char(CharT character)
|
||||
{
|
||||
if constexpr (std::is_same_v<CharT, wchar_t>)
|
||||
return static_cast<CharT>(std::towlower(static_cast<wint_t>(character)));
|
||||
else
|
||||
return static_cast<CharT>(std::tolower(static_cast<unsigned char>(character)));
|
||||
}
|
||||
}
|
||||
|
||||
bool path_is_exist(const std::string& path)
|
||||
std::filesystem::path path_preferences_get()
|
||||
{
|
||||
auto path = SDL_GetPrefPath(nullptr, "anm2ed");
|
||||
auto filePath = std::filesystem::path(path);
|
||||
SDL_free(path);
|
||||
return filePath;
|
||||
}
|
||||
|
||||
std::filesystem::path path_to_lower(const std::filesystem::path& path)
|
||||
{
|
||||
auto native = path.native();
|
||||
for (auto& character : native)
|
||||
character = to_lower_char(character);
|
||||
return std::filesystem::path(native);
|
||||
}
|
||||
|
||||
std::filesystem::path path_backslash_replace(const std::filesystem::path& path)
|
||||
{
|
||||
auto native = path.native();
|
||||
constexpr auto backslash = static_cast<std::filesystem::path::value_type>('\\');
|
||||
constexpr auto slash = static_cast<std::filesystem::path::value_type>('/');
|
||||
for (auto& character : native)
|
||||
if (character == backslash) character = slash;
|
||||
return std::filesystem::path(native);
|
||||
}
|
||||
|
||||
bool path_is_exist(const std::filesystem::path& path)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
return std::filesystem::exists(path, errorCode) && ((void)std::filesystem::status(path, errorCode), !errorCode);
|
||||
}
|
||||
|
||||
bool path_is_extension(const std::string& path, const std::string& extension)
|
||||
bool path_is_extension(const std::filesystem::path& path, const std::string& extension)
|
||||
{
|
||||
auto e = std::filesystem::path(path).extension().string();
|
||||
std::transform(e.begin(), e.end(), e.begin(), ::tolower);
|
||||
return e == ("." + extension);
|
||||
}
|
||||
|
||||
std::filesystem::path path_lower_case_backslash_handle(std::filesystem::path& path)
|
||||
std::filesystem::path path_lower_case_backslash_handle(const std::filesystem::path& path)
|
||||
{
|
||||
auto asString = path.generic_string();
|
||||
if (path_is_exist(asString)) return path;
|
||||
auto newPath = path;
|
||||
if (path_is_exist(newPath)) return newPath;
|
||||
|
||||
asString = string::backslash_replace(asString);
|
||||
path = asString;
|
||||
if (path_is_exist(asString)) return path;
|
||||
newPath = path_backslash_replace(newPath);
|
||||
if (path_is_exist(newPath)) return newPath;
|
||||
|
||||
asString = string::to_lower(asString);
|
||||
path = asString;
|
||||
return path;
|
||||
newPath = path_to_lower(newPath);
|
||||
return newPath;
|
||||
}
|
||||
|
||||
WorkingDirectory::WorkingDirectory(const std::string& path, bool isFile)
|
||||
WorkingDirectory::WorkingDirectory(const std::filesystem::path& path, bool isFile)
|
||||
{
|
||||
previous = std::filesystem::current_path();
|
||||
if (isFile)
|
||||
@@ -56,10 +84,5 @@ namespace anm2ed::util::filesystem
|
||||
std::filesystem::current_path(path);
|
||||
}
|
||||
|
||||
WorkingDirectory::WorkingDirectory(const std::filesystem::path& path, bool isFile)
|
||||
: WorkingDirectory(path.string(), isFile)
|
||||
{
|
||||
}
|
||||
|
||||
WorkingDirectory::~WorkingDirectory() { std::filesystem::current_path(previous); }
|
||||
}
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
|
||||
namespace anm2ed::util::filesystem
|
||||
{
|
||||
std::string path_preferences_get();
|
||||
std::filesystem::path path_preferences_get();
|
||||
std::filesystem::path path_to_lower(const std::filesystem::path&);
|
||||
std::filesystem::path path_backslash_replace(const std::filesystem::path&);
|
||||
|
||||
bool path_is_exist(const std::string&);
|
||||
bool path_is_extension(const std::string&, const std::string&);
|
||||
bool path_is_exist(const std::filesystem::path&);
|
||||
bool path_is_extension(const std::filesystem::path&, const std::string&);
|
||||
|
||||
std::filesystem::path path_lower_case_backslash_handle(std::filesystem::path&);
|
||||
std::filesystem::path path_lower_case_backslash_handle(const std::filesystem::path&);
|
||||
|
||||
class WorkingDirectory
|
||||
{
|
||||
public:
|
||||
std::filesystem::path previous;
|
||||
|
||||
WorkingDirectory(const std::string&, bool = false);
|
||||
WorkingDirectory(const std::filesystem::path&, bool = false);
|
||||
~WorkingDirectory();
|
||||
};
|
||||
|
||||
@@ -19,13 +19,7 @@ namespace anm2ed::util::string
|
||||
return transformed;
|
||||
}
|
||||
|
||||
std::string quote(const std::string& string)
|
||||
{
|
||||
return "\"" + string + "\"";
|
||||
}
|
||||
std::string quote(const std::string& string) { return "\"" + string + "\""; }
|
||||
|
||||
bool to_bool(const std::string& string)
|
||||
{
|
||||
return to_lower(string) == "true";
|
||||
}
|
||||
bool to_bool(const std::string& string) { return to_lower(string) == "true"; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user