Sound drag/drop

This commit is contained in:
2025-11-30 22:25:57 -05:00
parent d0221928aa
commit 300e322b9d
49 changed files with 1280230 additions and 210091 deletions

View File

@@ -13,7 +13,7 @@ namespace anm2ed::anm2
class Animation
{
public:
std::string name{"New Animation"};
std::string name{};
int frameNum{FRAME_NUM_MIN};
bool isLoop{true};
Item rootAnimation;

View File

@@ -38,9 +38,12 @@ namespace anm2ed::anm2
std::vector<std::string> Anm2::spritesheet_labels_get()
{
std::vector<std::string> labels{};
labels.emplace_back("None");
labels.emplace_back(localize.get(BASIC_NONE));
for (auto& [id, spritesheet] : content.spritesheets)
labels.emplace_back(std::format(SPRITESHEET_FORMAT, id, spritesheet.path.string()));
{
auto string = spritesheet.path.string();
labels.emplace_back(std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, string)));
}
return labels;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "icon.h"
#include "strings.h"
#include <glm/glm/vec2.hpp>
#include <glm/glm/vec3.hpp>
@@ -8,28 +9,30 @@
namespace anm2ed::anm2
{
extern const glm::vec4 ROOT_COLOR;
extern const glm::vec4 ROOT_COLOR_ACTIVE;
extern const glm::vec4 ROOT_COLOR_HOVERED;
inline const glm::vec4 ROOT_COLOR = glm::vec4(0.140f, 0.310f, 0.560f, 1.000f);
inline const glm::vec4 ROOT_COLOR_ACTIVE = glm::vec4(0.240f, 0.520f, 0.880f, 1.000f);
inline const glm::vec4 ROOT_COLOR_HOVERED = glm::vec4(0.320f, 0.640f, 1.000f, 1.000f);
extern const glm::vec4 LAYER_COLOR;
extern const glm::vec4 LAYER_COLOR_ACTIVE;
extern const glm::vec4 LAYER_COLOR_HOVERED;
inline const glm::vec4 LAYER_COLOR = glm::vec4(0.640f, 0.320f, 0.110f, 1.000f);
inline const glm::vec4 LAYER_COLOR_ACTIVE = glm::vec4(0.840f, 0.450f, 0.170f, 1.000f);
inline const glm::vec4 LAYER_COLOR_HOVERED = glm::vec4(0.960f, 0.560f, 0.240f, 1.000f);
extern const glm::vec4 NULL_COLOR;
extern const glm::vec4 NULL_COLOR_ACTIVE;
extern const glm::vec4 NULL_COLOR_HOVERED;
inline const glm::vec4 NULL_COLOR = glm::vec4(0.140f, 0.430f, 0.200f, 1.000f);
inline const glm::vec4 NULL_COLOR_ACTIVE = glm::vec4(0.250f, 0.650f, 0.350f, 1.000f);
inline const glm::vec4 NULL_COLOR_HOVERED = glm::vec4(0.350f, 0.800f, 0.480f, 1.000f);
extern const glm::vec4 TRIGGER_COLOR;
extern const glm::vec4 TRIGGER_COLOR_ACTIVE;
extern const glm::vec4 TRIGGER_COLOR_HOVERED;
inline const glm::vec4 TRIGGER_COLOR = glm::vec4(0.620f, 0.150f, 0.260f, 1.000f);
inline const glm::vec4 TRIGGER_COLOR_ACTIVE = glm::vec4(0.820f, 0.250f, 0.380f, 1.000f);
inline const glm::vec4 TRIGGER_COLOR_HOVERED = glm::vec4(0.950f, 0.330f, 0.490f, 1.000f);
#define TYPE_LIST \
X(NONE, "", "", resource::icon::NONE, glm::vec4(), glm::vec4(), glm::vec4()) \
X(ROOT, "Root", "RootAnimation", resource::icon::ROOT, ROOT_COLOR, ROOT_COLOR_ACTIVE, ROOT_COLOR_HOVERED) \
X(LAYER, "Layer", "LayerAnimation", resource::icon::LAYER, LAYER_COLOR, LAYER_COLOR_ACTIVE, LAYER_COLOR_HOVERED) \
X(NULL_, "Null", "NullAnimation", resource::icon::NULL_, NULL_COLOR, NULL_COLOR_ACTIVE, NULL_COLOR_HOVERED) \
X(TRIGGER, "Trigger", "Triggers", resource::icon::TRIGGERS, TRIGGER_COLOR, TRIGGER_COLOR_ACTIVE, \
X(NONE, STRING_UNDEFINED, "", resource::icon::NONE, glm::vec4(), glm::vec4(), glm::vec4()) \
X(ROOT, BASIC_ROOT, "RootAnimation", resource::icon::ROOT, ROOT_COLOR, ROOT_COLOR_ACTIVE, ROOT_COLOR_HOVERED) \
X(LAYER, BASIC_LAYER_ANIMATION, "LayerAnimation", resource::icon::LAYER, LAYER_COLOR, LAYER_COLOR_ACTIVE, \
LAYER_COLOR_HOVERED) \
X(NULL_, BASIC_NULL_ANIMATION, "NullAnimation", resource::icon::NULL_, NULL_COLOR, NULL_COLOR_ACTIVE, \
NULL_COLOR_HOVERED) \
X(TRIGGER, BASIC_TRIGGERS, "Triggers", resource::icon::TRIGGERS, TRIGGER_COLOR, TRIGGER_COLOR_ACTIVE, \
TRIGGER_COLOR_HOVERED)
enum Type
@@ -39,7 +42,7 @@ namespace anm2ed::anm2
#undef X
};
constexpr const char* TYPE_STRINGS[] = {
constexpr StringType TYPE_STRINGS[] = {
#define X(symbol, string, itemString, icon, color, colorActive, colorHovered) string,
TYPE_LIST
#undef X
@@ -57,9 +60,23 @@ namespace anm2ed::anm2
#undef X
};
extern const glm::vec4 TYPE_COLOR[];
extern const glm::vec4 TYPE_COLOR_ACTIVE[];
extern const glm::vec4 TYPE_COLOR_HOVERED[];
inline const glm::vec4 TYPE_COLOR[] = {
#define X(symbol, string, itemString, icon, color, colorActive, colorHovered) color,
TYPE_LIST
#undef X
};
inline const glm::vec4 TYPE_COLOR_ACTIVE[] = {
#define X(symbol, string, itemString, icon, color, colorActive, colorHovered) colorActive,
TYPE_LIST
#undef X
};
inline const glm::vec4 TYPE_COLOR_HOVERED[] = {
#define X(symbol, string, itemString, icon, color, colorActive, colorHovered) colorHovered,
TYPE_LIST
#undef X
};
enum ChangeType
{
@@ -69,4 +86,4 @@ namespace anm2ed::anm2
MULTIPLY,
DIVIDE
};
}
}

View File

@@ -8,7 +8,7 @@ namespace anm2ed::anm2
class Event
{
public:
std::string name{"New Event"};
std::string name{};
int soundID{-1};
Event() = default;

View File

@@ -5,13 +5,10 @@
namespace anm2ed::anm2
{
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
constexpr auto LAYER_NO_SPRITESHEET_FORMAT = "#{} {} (No Spritesheet)";
class Layer
{
public:
std::string name{"New Layer"};
std::string name{};
int spritesheetID{};
Layer() = default;

View File

@@ -5,12 +5,10 @@
namespace anm2ed::anm2
{
constexpr auto NULL_FORMAT = "#{} {}";
class Null
{
public:
std::string name{"New Null"};
std::string name{};
bool isShowRect{};
Null() = default;

View File

@@ -7,8 +7,6 @@
namespace anm2ed::anm2
{
constexpr auto SOUND_FORMAT = "{}";
class Sound
{
public:

View File

@@ -8,9 +8,6 @@
namespace anm2ed::anm2
{
constexpr auto SPRITESHEET_FORMAT_C = "#%d %s";
constexpr auto SPRITESHEET_FORMAT = "#{} {}";
class Spritesheet
{
public:

View File

@@ -13,7 +13,6 @@ namespace anm2ed::canvas
constexpr auto PIVOT_SIZE = glm::vec2(8, 8);
constexpr auto ZOOM_MIN = 1.0f;
constexpr auto ZOOM_MAX = 2000.0f;
constexpr auto POSITION_FORMAT = "Position: ({:8}, {:8})";
constexpr auto DASH_LENGTH = 4.0f;
constexpr auto DASH_GAP = 1.0f;

View File

@@ -4,6 +4,8 @@
#include <windows.h>
#elif __unix__
#else
#include "log.h"
#include "strings.h"
#include "toast.h"
#endif
@@ -63,7 +65,8 @@ namespace anm2ed
#elif __unix__
system(std::format("xdg-open \"{}\" &", path).c_str());
#else
toasts.info("Operation not supported.");
toasts.push(localize.get(TOAST_NOT_SUPPORTED));
logger.warning(localize.get(TOAST_NOT_SUPPORTED, anm2ed::ENGLISH));
#endif
}

View File

@@ -2,7 +2,12 @@
#include <utility>
#include <format>
#include <format>
#include "log.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::anm2;
@@ -79,14 +84,22 @@ namespace anm2ed
{
this->path = !path.empty() ? path : this->path.string();
if (anm2.serialize(this->path.string(), errorString))
auto absolutePath = this->path.string();
if (anm2.serialize(absolutePath, errorString))
{
toasts.info(std::format("Saved document to: {}", this->path.string()));
toasts.push(std::vformat(localize.get(TOAST_SAVE_DOCUMENT), std::make_format_args(absolutePath)));
logger.info(std::vformat(localize.get(TOAST_SAVE_DOCUMENT, anm2ed::ENGLISH),
std::make_format_args(absolutePath)));
clean();
return true;
}
else if (errorString)
toasts.warning(std::format("Could not save document to: {} ({})", this->path.string(), *errorString));
{
toasts.push(std::vformat(localize.get(TOAST_SAVE_DOCUMENT_FAILED),
std::make_format_args(absolutePath, *errorString)));
logger.error(std::vformat(localize.get(TOAST_SAVE_DOCUMENT_FAILED, anm2ed::ENGLISH),
std::make_format_args(absolutePath, *errorString)));
}
return false;
}
@@ -110,16 +123,23 @@ namespace anm2ed
bool Document::autosave(std::string* errorString)
{
auto autosavePath = autosave_path_get();
if (anm2.serialize(autosavePath.string(), errorString))
auto autosavePathString = autosavePath.string();
if (anm2.serialize(autosavePathString, errorString))
{
autosaveHash = hash;
lastAutosaveTime = 0.0f;
toasts.info("Autosaving...");
toasts.push(localize.get(TOAST_AUTOSAVING));
logger.info(localize.get(TOAST_AUTOSAVING, anm2ed::ENGLISH));
logger.info(std::format("Autosaved document to: {}", autosavePath.string()));
return true;
}
else if (errorString)
toasts.warning(std::format("Could not autosave document to: {} ({})", autosavePath.string(), *errorString));
{
toasts.push(std::vformat(localize.get(TOAST_AUTOSAVE_FAILED),
std::make_format_args(autosavePathString, *errorString)));
logger.error(std::vformat(localize.get(TOAST_AUTOSAVE_FAILED, anm2ed::ENGLISH),
std::make_format_args(autosavePathString, *errorString)));
}
return false;
}
@@ -224,18 +244,53 @@ namespace anm2ed
auto add = [&]()
{
int id{};
auto pathCopy = path;
if (anm2.spritesheet_add(directory_get().string(), path, id))
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
auto path = spritesheet.path.string();
this->spritesheet.selection = {id};
this->spritesheet.reference = id;
toasts.info(std::format("Initialized spritesheet #{}: {}", id, spritesheet.path.string()));
toasts.push(std::vformat(localize.get(TOAST_SPRITESHEET_INITIALIZED), std::make_format_args(id, path)));
logger.info(std::vformat(localize.get(TOAST_SPRITESHEET_INITIALIZED, anm2ed::ENGLISH),
std::make_format_args(id, path)));
}
else
toasts.error(std::format("Failed to initialize spritesheet: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_SPRITESHEET_INIT_FAILED), std::make_format_args(pathCopy)));
logger.error(std::vformat(localize.get(TOAST_SPRITESHEET_INIT_FAILED, anm2ed::ENGLISH),
std::make_format_args(pathCopy)));
}
};
DOCUMENT_EDIT_PTR(this, "Add Spritesheet", Document::SPRITESHEETS, add());
DOCUMENT_EDIT_PTR(this, localize.get(EDIT_ADD_SPRITESHEET), Document::SPRITESHEETS, add());
}
void Document::sound_add(const std::string& path)
{
auto add = [&]()
{
int id{};
auto pathCopy = path;
if (anm2.sound_add(directory_get().string(), path, id))
{
auto& soundInfo = anm2.content.sounds[id];
auto soundPath = soundInfo.path.string();
sound.selection = {id};
sound.reference = id;
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZED), std::make_format_args(id, soundPath)));
logger.info(std::vformat(localize.get(TOAST_SOUND_INITIALIZED, anm2ed::ENGLISH),
std::make_format_args(id, soundPath)));
}
else
{
toasts.push(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED), std::make_format_args(pathCopy)));
logger.error(std::vformat(localize.get(TOAST_SOUND_INITIALIZE_FAILED, anm2ed::ENGLISH),
std::make_format_args(pathCopy)));
}
};
DOCUMENT_EDIT_PTR(this, localize.get(EDIT_ADD_SOUND), Document::SOUNDS, add());
}
void Document::snapshot(const std::string& message)
@@ -247,14 +302,16 @@ namespace anm2ed
void Document::undo()
{
snapshots.undo();
toasts.info(std::format("Undo: {}", message));
toasts.push(std::vformat(localize.get(TOAST_UNDO), std::make_format_args(message)));
logger.info(std::vformat(localize.get(TOAST_UNDO, anm2ed::ENGLISH), std::make_format_args(message)));
change(Document::ALL);
}
void Document::redo()
{
snapshots.redo();
toasts.info(std::format("Redo: {}", message));
toasts.push(std::vformat(localize.get(TOAST_REDO), std::make_format_args(message)));
logger.info(std::vformat(localize.get(TOAST_REDO, anm2ed::ENGLISH), std::make_format_args(message)));
change(Document::ALL);
}

View File

@@ -85,6 +85,7 @@ namespace anm2ed
anm2::Animation* animation_get();
void spritesheet_add(const std::string&);
void sound_add(const std::string&);
bool autosave(std::string* = nullptr);
std::filesystem::path autosave_path_get();

View File

@@ -1,7 +1,9 @@
#include "documents.h"
#include <format>
#include <vector>
#include "strings.h"
#include "time_.h"
using namespace anm2ed::resource;
@@ -44,7 +46,7 @@ namespace anm2ed::imgui
{
height = ImGui::GetWindowSize().y;
if (ImGui::BeginTabBar("Documents Bar", ImGuiTabBarFlags_Reorderable))
if (ImGui::BeginTabBar("##Documents Bar", ImGuiTabBarFlags_Reorderable))
{
auto documentsCount = (int)manager.documents.size();
bool isCloseShortcut = shortcut(manager.chords[SHORTCUT_CLOSE], shortcut::GLOBAL) && !closePopup.is_open();
@@ -84,8 +86,9 @@ namespace anm2ed::imgui
auto isRequested = i == manager.pendingSelected;
auto font = isDirty ? font::ITALICS : font::REGULAR;
auto string = isDirty ? std::format("[Not Saved] {}", document.filename_get().string())
: document.filename_get().string();
auto filename = document.filename_get().string();
auto string =
isDirty ? std::vformat(localize.get(FORMAT_NOT_SAVED), std::make_format_args(filename)) : filename;
auto label = std::format("{}###Document{}", string, i);
auto flags = isDirty ? ImGuiTabItemFlags_UnsavedDocument : 0;
@@ -116,15 +119,15 @@ namespace anm2ed::imgui
closePopup.trigger();
if (ImGui::BeginPopupModal(closePopup.label, &closePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(closePopup.label(), &closePopup.isOpen, ImGuiWindowFlags_NoResize))
{
if (closeDocumentIndex >= 0 && closeDocumentIndex < (int)manager.documents.size())
{
auto& closeDocument = manager.documents[closeDocumentIndex];
ImGui::TextUnformatted(std::format("The document \"{}\" has been modified.\nDo you want to save it?",
closeDocument.filename_get().string())
.c_str());
auto filename = closeDocument.filename_get().string();
auto prompt = std::vformat(localize.get(LABEL_DOCUMENT_MODIFIED_PROMPT), std::make_format_args(filename));
ImGui::TextUnformatted(prompt.c_str());
auto widgetSize = imgui::widget_size_with_row_get(3);
@@ -134,7 +137,7 @@ namespace anm2ed::imgui
closePopup.close();
};
if (ImGui::Button("Yes", widgetSize))
if (ImGui::Button(localize.get(BASIC_YES), widgetSize))
{
manager.save(closeDocumentIndex);
manager.close(closeDocumentIndex);
@@ -143,7 +146,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("No", widgetSize))
if (ImGui::Button(localize.get(BASIC_NO), widgetSize))
{
manager.close(closeDocumentIndex);
close();
@@ -151,7 +154,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize))
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize))
{
isQuitting = false;
close();
@@ -187,17 +190,18 @@ namespace anm2ed::imgui
manager.anm2DragDropPopup.trigger();
if (ImGui::BeginPopupContextWindow(manager.anm2DragDropPopup.label, ImGuiPopupFlags_None))
if (ImGui::BeginPopupContextWindow(manager.anm2DragDropPopup.label(), ImGuiPopupFlags_None))
{
auto document = manager.get();
if (ImGui::MenuItem(manager.anm2DragDropPaths.size() > 1 ? "Open Many Documents" : "Open New Document"))
if (ImGui::MenuItem(manager.anm2DragDropPaths.size() > 1 ? localize.get(LABEL_DOCUMENTS_OPEN_MANY)
: localize.get(LABEL_DOCUMENTS_OPEN_NEW)))
{
for (auto& path : manager.anm2DragDropPaths)
manager.open(path);
drag_drop_reset();
}
if (ImGui::MenuItem("Merge into Current Document", nullptr, false,
if (ImGui::MenuItem(localize.get(LABEL_DOCUMENTS_MERGE_INTO_CURRENT), nullptr, false,
document && !manager.anm2DragDropPaths.empty()))
{
if (document)
@@ -211,17 +215,19 @@ namespace anm2ed::imgui
}
};
DOCUMENT_EDIT_PTR(document, "Merge Anm2", Document::ALL, merge_anm2s());
DOCUMENT_EDIT_PTR(document, localize.get(EDIT_MERGE_ANM2), Document::ALL, merge_anm2s());
drag_drop_reset();
}
}
if (ImGui::MenuItem("Cancel")) drag_drop_reset();
ImGui::Separator();
if (ImGui::MenuItem(localize.get(BASIC_CANCEL))) drag_drop_reset();
manager.anm2DragDropPopup.end();
ImGui::EndPopup();
}
else if (!ImGui::IsPopupOpen(manager.anm2DragDropPopup.label))
else if (!ImGui::IsPopupOpen(manager.anm2DragDropPopup.label()))
drag_drop_reset();
}
}

View File

@@ -3,6 +3,7 @@
#include "manager.h"
#include "resources.h"
#include "settings.h"
#include "strings.h"
#include "taskbar.h"
namespace anm2ed::imgui
@@ -10,7 +11,7 @@ namespace anm2ed::imgui
class Documents
{
int closeDocumentIndex{-1};
imgui::PopupHelper closePopup{imgui::PopupHelper("Close Document", imgui::POPUP_TO_CONTENT)};
imgui::PopupHelper closePopup{imgui::PopupHelper(LABEL_DOCUMENT_CLOSE, imgui::POPUP_TO_CONTENT)};
public:
float height{};

View File

@@ -1,4 +1,5 @@
#include "imgui_.h"
#include "strings.h"
#include <imgui/imgui_internal.h>
@@ -161,7 +162,8 @@ namespace anm2ed::imgui
void set_item_tooltip_shortcut(const char* tooltip, const std::string& shortcut)
{
ImGui::SetItemTooltip("%s\n(Shortcut: %s)", tooltip, shortcut.c_str());
ImGui::SetItemTooltip(
"%s", std::vformat(localize.get(FORMAT_TOOLTIP_SHORTCUT), std::make_format_args(tooltip, shortcut)).c_str());
}
namespace
@@ -343,9 +345,9 @@ namespace anm2ed::imgui
return (*indexMap)[index];
}
PopupHelper::PopupHelper(const char* label, PopupType type, PopupPosition position)
PopupHelper::PopupHelper(StringType labelId, PopupType type, PopupPosition position)
{
this->label = label;
this->labelId = labelId;
this->type = type;
this->position = position;
}
@@ -361,7 +363,7 @@ namespace anm2ed::imgui
void PopupHelper::trigger()
{
if (isTriggered) ImGui::OpenPopup(label);
if (isTriggered) ImGui::OpenPopup(localize.get(labelId));
isTriggered = false;
auto viewport = ImGui::GetMainViewport();

View File

@@ -7,6 +7,7 @@
#include <unordered_map>
#include <vector>
#include "strings.h"
#include "types.h"
namespace anm2ed::imgui
@@ -216,14 +217,15 @@ namespace anm2ed::imgui
class PopupHelper
{
public:
const char* label{};
StringType labelId{};
PopupType type{};
PopupPosition position{};
bool isOpen{};
bool isTriggered{};
bool isJustOpened{};
PopupHelper(const char*, PopupType = POPUP_NORMAL, PopupPosition = POPUP_CENTER);
PopupHelper(StringType, PopupType = POPUP_NORMAL, PopupPosition = POPUP_CENTER);
const char* label() const { return localize.get(labelId); }
bool is_open();
void open();
void trigger();

View File

@@ -13,6 +13,7 @@
#include <imgui/imgui.h>
#include "log.h"
#include "math_.h"
#include "render.h"
#include "shader.h"
@@ -22,6 +23,8 @@
#include "icon.h"
#include "toast.h"
#include "strings.h"
using namespace anm2ed::resource;
using namespace anm2ed::types;
using namespace anm2ed::canvas;
@@ -30,8 +33,76 @@ using namespace glm;
namespace anm2ed::imgui
{
static constexpr auto ANM2ED_LABEL = "Anm2Ed";
static constexpr auto VERSION_LABEL = "Version 2.0";
namespace
{
#ifndef _WIN32
constexpr auto EXEC_PERMS =
std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec;
#endif
bool ffmpeg_is_executable(const std::string& pathString)
{
if (pathString.empty()) return false;
std::error_code ec{};
auto status = std::filesystem::status(pathString, ec);
if (ec || !std::filesystem::is_regular_file(status)) return false;
#ifndef _WIN32
if ((status.permissions() & EXEC_PERMS) == std::filesystem::perms::none) return false;
#endif
return true;
}
bool png_directory_ensure(const std::string& directory)
{
if (directory.empty())
{
toasts.push(localize.get(TOAST_PNG_DIRECTORY_NOT_SET));
logger.warning(localize.get(TOAST_PNG_DIRECTORY_NOT_SET, anm2ed::ENGLISH));
return false;
}
std::error_code ec{};
auto pathValue = std::filesystem::path(directory);
auto exists = std::filesystem::exists(pathValue, ec);
if (ec)
{
auto errorMessage = ec.message();
toasts.push(std::vformat(localize.get(TOAST_PNG_DIRECTORY_ACCESS_ERROR),
std::make_format_args(directory, errorMessage)));
logger.error(std::vformat(localize.get(TOAST_PNG_DIRECTORY_ACCESS_ERROR, anm2ed::ENGLISH),
std::make_format_args(directory, errorMessage)));
return false;
}
if (exists)
{
if (!std::filesystem::is_directory(pathValue, ec) || ec)
{
toasts.push(std::vformat(localize.get(TOAST_PNG_DIRECTORY_NOT_DIRECTORY), std::make_format_args(directory)));
logger.warning(std::vformat(localize.get(TOAST_PNG_DIRECTORY_NOT_DIRECTORY, anm2ed::ENGLISH),
std::make_format_args(directory)));
return false;
}
return true;
}
if (!std::filesystem::create_directories(pathValue, ec) || ec)
{
auto errorMessage = ec.message();
toasts.push(std::vformat(localize.get(TOAST_PNG_DIRECTORY_CREATE_ERROR),
std::make_format_args(directory, errorMessage)));
logger.error(std::vformat(localize.get(TOAST_PNG_DIRECTORY_CREATE_ERROR, anm2ed::ENGLISH),
std::make_format_args(directory, errorMessage)));
return false;
}
return true;
}
}
static constexpr auto CREDIT_DELAY = 1.0f;
static constexpr auto CREDIT_SCROLL_SPEED = 25.0f;
@@ -64,6 +135,10 @@ namespace anm2ed::imgui
{"Additional Help", font::BOLD},
{"im-tem"},
{""},
{"Localization", font::BOLD},
{"ExtremeThreat (Russian)"},
{"sawalk/사왈이 (Korean)"},
{""},
{"Based on the work of:", font::BOLD},
{"Adrian Gavrilita"},
{"Simon Parzer"},
@@ -122,12 +197,21 @@ namespace anm2ed::imgui
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{"enjoy the jams :)"},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
{""},
};
static constexpr auto CREDIT_COUNT = (int)(sizeof(CREDITS) / sizeof(Credit));
@@ -143,13 +227,14 @@ namespace anm2ed::imgui
{
height = ImGui::GetWindowSize().y;
if (ImGui::BeginMenu("File"))
if (ImGui::BeginMenu(localize.get(LABEL_FILE_MENU)))
{
if (ImGui::MenuItem("New", settings.shortcutNew.c_str())) dialog.file_save(dialog::ANM2_NEW);
if (ImGui::MenuItem("Open", settings.shortcutOpen.c_str())) dialog.file_open(dialog::ANM2_OPEN);
if (ImGui::MenuItem(localize.get(BASIC_NEW), settings.shortcutNew.c_str())) dialog.file_save(dialog::ANM2_NEW);
if (ImGui::MenuItem(localize.get(BASIC_OPEN), settings.shortcutOpen.c_str()))
dialog.file_open(dialog::ANM2_OPEN);
auto recentFiles = manager.recent_files_ordered();
if (ImGui::BeginMenu("Open Recent", !recentFiles.empty()))
if (ImGui::BeginMenu(localize.get(LABEL_OPEN_RECENT), !recentFiles.empty()))
{
for (std::size_t index = 0; index < recentFiles.size(); ++index)
{
@@ -162,21 +247,26 @@ namespace anm2ed::imgui
}
if (!recentFiles.empty())
if (ImGui::MenuItem("Clear List")) manager.recent_files_clear();
if (ImGui::MenuItem(localize.get(LABEL_CLEAR_LIST))) manager.recent_files_clear();
ImGui::EndMenu();
}
if (ImGui::MenuItem("Save", settings.shortcutSave.c_str(), false, document))
if (settings.fileIsWarnOverwrite) overwritePopup.open();
if (ImGui::MenuItem(localize.get(BASIC_SAVE), settings.shortcutSave.c_str(), false, document))
{
if (settings.fileIsWarnOverwrite)
overwritePopup.open();
else
manager.save(document->path);
}
if (ImGui::MenuItem("Save As", settings.shortcutSaveAs.c_str(), false, document))
if (ImGui::MenuItem(localize.get(LABEL_SAVE_AS), settings.shortcutSaveAs.c_str(), false, document))
dialog.file_save(dialog::ANM2_SAVE);
if (ImGui::MenuItem("Explore XML Location", nullptr, false, document))
if (ImGui::MenuItem(localize.get(LABEL_EXPLORE_XML_LOCATION), nullptr, false, document))
dialog.file_explorer_open(document->directory_get().string());
ImGui::Separator();
if (ImGui::MenuItem("Exit", settings.shortcutExit.c_str())) isQuitting = true;
if (ImGui::MenuItem(localize.get(LABEL_EXIT), settings.shortcutExit.c_str())) isQuitting = true;
ImGui::EndMenu();
}
if (dialog.is_selected(dialog::ANM2_NEW))
@@ -197,43 +287,43 @@ namespace anm2ed::imgui
dialog.reset();
}
if (ImGui::BeginMenu("Wizard"))
if (ImGui::BeginMenu(localize.get(LABEL_WIZARD_MENU)))
{
if (ImGui::MenuItem("Generate Animation From Grid", nullptr, false,
if (ImGui::MenuItem(localize.get(LABEL_TASKBAR_GENERATE_ANIMATION_FROM_GRID), nullptr, false,
item && document->reference.itemType == anm2::LAYER))
generatePopup.open();
ImGui::Separator();
if (ImGui::MenuItem("Render Animation", nullptr, false, animation)) renderPopup.open();
if (ImGui::MenuItem(localize.get(LABEL_TASKBAR_RENDER_ANIMATION), nullptr, false, animation))
renderPopup.open();
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Playback"))
if (ImGui::BeginMenu(localize.get(LABEL_PLAYBACK_MENU)))
{
ImGui::MenuItem("Always Loop", nullptr, &settings.playbackIsLoop);
ImGui::SetItemTooltip("%s", "Animations will always loop during playback, even if looping isn't set.");
ImGui::MenuItem(localize.get(LABEL_PLAYBACK_ALWAYS_LOOP), nullptr, &settings.playbackIsLoop);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PLAYBACK_ALWAYS_LOOP));
ImGui::MenuItem("Clamp", nullptr, &settings.playbackIsClamp);
ImGui::SetItemTooltip("%s", "Operations will always be clamped to within the animation's bounds.\nFor example, "
"dragging the playhead, or triggers.");
ImGui::MenuItem(localize.get(LABEL_CLAMP), nullptr, &settings.playbackIsClamp);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PLAYBACK_CLAMP));
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Window"))
if (ImGui::BeginMenu(localize.get(LABEL_WINDOW_MENU)))
{
for (std::size_t index = 0; index < WINDOW_COUNT; ++index)
{
auto member = WINDOW_MEMBERS[index];
ImGui::MenuItem(WINDOW_STRINGS[index], nullptr, &(settings.*member));
ImGui::MenuItem(localize.get(::anm2ed::WINDOW_STRING_TYPES[index]), nullptr, &(settings.*member));
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Settings"))
if (ImGui::BeginMenu(localize.get(LABEL_SETTINGS_MENU)))
{
if (ImGui::MenuItem("Configure"))
if (ImGui::MenuItem(localize.get(LABEL_TASKBAR_CONFIGURE)))
{
editSettings = settings;
configurePopup.open();
@@ -242,10 +332,10 @@ namespace anm2ed::imgui
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Help"))
if (ImGui::BeginMenu(localize.get(LABEL_HELP_MENU)))
{
if (ImGui::MenuItem("About")) aboutPopup.open();
if (ImGui::MenuItem(localize.get(LABEL_TASKBAR_ABOUT))) aboutPopup.open();
ImGui::EndMenu();
}
@@ -254,7 +344,7 @@ namespace anm2ed::imgui
generatePopup.trigger();
if (ImGui::BeginPopupModal(generatePopup.label, &generatePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(generatePopup.label(), &generatePopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto& startPosition = settings.generateStartPosition;
auto& size = settings.generateSize;
@@ -270,15 +360,15 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Options Child", childSize, ImGuiChildFlags_Borders))
{
ImGui::InputInt2("Start Position", value_ptr(startPosition));
ImGui::InputInt2("Frame Size", value_ptr(size));
ImGui::InputInt2("Pivot", value_ptr(pivot));
ImGui::InputInt("Rows", &rows, STEP, STEP_FAST);
ImGui::InputInt("Columns", &columns, STEP, STEP_FAST);
ImGui::InputInt2(localize.get(LABEL_GENERATE_START_POSITION), value_ptr(startPosition));
ImGui::InputInt2(localize.get(LABEL_GENERATE_FRAME_SIZE), value_ptr(size));
ImGui::InputInt2(localize.get(BASIC_PIVOT), value_ptr(pivot));
ImGui::InputInt(localize.get(LABEL_GENERATE_ROWS), &rows, STEP, STEP_FAST);
ImGui::InputInt(localize.get(LABEL_GENERATE_COLUMNS), &columns, STEP, STEP_FAST);
input_int_range("Count", count, anm2::FRAME_NUM_MIN, rows * columns);
input_int_range(localize.get(LABEL_GENERATE_COUNT), count, anm2::FRAME_NUM_MIN, rows * columns);
ImGui::InputInt("Duration", &delay, STEP, STEP_FAST);
ImGui::InputInt(localize.get(BASIC_DURATION), &delay, STEP, STEP_FAST);
}
ImGui::EndChild();
@@ -331,7 +421,7 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Generate", widgetSize))
if (ImGui::Button(localize.get(LABEL_GENERATE), widgetSize))
{
auto generate_from_grid = [&]()
{
@@ -339,41 +429,46 @@ namespace anm2ed::imgui
animation->frameNum = animation->length();
};
DOCUMENT_EDIT_PTR(document, "Generate Animation from Grid", Document::FRAMES, generate_from_grid());
DOCUMENT_EDIT_PTR(document, localize.get(EDIT_GENERATE_ANIMATION_FROM_GRID), Document::FRAMES,
generate_from_grid());
generatePopup.close();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) generatePopup.close();
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) generatePopup.close();
ImGui::EndPopup();
}
configurePopup.trigger();
if (ImGui::BeginPopupModal(configurePopup.label, &configurePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(configurePopup.label(), &configurePopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto childSize = size_without_footer_get(2);
if (ImGui::BeginTabBar("##Configure Tabs"))
{
if (ImGui::BeginTabItem("Display"))
if (ImGui::BeginTabItem(localize.get(LABEL_DISPLAY)))
{
if (ImGui::BeginChild("##Tab Child", childSize, true))
{
input_float_range("UI Scale", editSettings.uiScale, 0.5f, 2.0f, 0.25f, 0.25f, "%.2f");
ImGui::SetItemTooltip("Change the scale of the UI.");
ImGui::SeparatorText(localize.get(LABEL_WINDOW_MENU));
input_float_range(localize.get(LABEL_UI_SCALE), editSettings.uiScale, 0.5f, 2.0f, 0.25f, 0.25f, "%.2f");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_UI_SCALE));
ImGui::Checkbox(localize.get(LABEL_VSYNC), &editSettings.isVsync);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_VSYNC));
ImGui::Checkbox("Vsync", &editSettings.isVsync);
ImGui::SetItemTooltip("Toggle vertical sync; synchronizes program update rate with monitor refresh rate.");
ImGui::SeparatorText(localize.get(LABEL_LOCALIZATION));
ImGui::Combo(localize.get(LABEL_LANGUAGE), &editSettings.language, LANGUAGE_STRINGS, LANGUAGE_COUNT);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LANGUAGE));
ImGui::SeparatorText("Theme");
ImGui::SeparatorText(localize.get(LABEL_THEME));
for (int i = 0; i < theme::COUNT; i++)
{
ImGui::RadioButton(theme::STRINGS[i], &editSettings.theme, i);
ImGui::RadioButton(localize.get(theme::STRINGS[i]), &editSettings.theme, i);
ImGui::SameLine();
}
}
@@ -382,76 +477,75 @@ namespace anm2ed::imgui
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("File"))
if (ImGui::BeginTabItem(localize.get(LABEL_FILE_MENU)))
{
if (ImGui::BeginChild("##Tab Child", childSize, true))
{
ImGui::SeparatorText("Autosave");
ImGui::SeparatorText(localize.get(LABEL_AUTOSAVE));
ImGui::Checkbox("Enabled", &editSettings.fileIsAutosave);
ImGui::SetItemTooltip(
"Enables autosaving of documents.\n(Does not overwrite files; makes copies to restore later.)");
ImGui::Checkbox(localize.get(BASIC_ENABLED), &editSettings.fileIsAutosave);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_AUTOSAVE_ENABLED));
ImGui::BeginDisabled(!editSettings.fileIsAutosave);
input_int_range("Time (minutes)", editSettings.fileAutosaveTime, 0, 10);
ImGui::SetItemTooltip("If changed, will autosave documents using this interval.");
input_int_range(localize.get(LABEL_TIME_MINUTES), editSettings.fileAutosaveTime, 0, 10);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_AUTOSAVE_INTERVAL));
ImGui::EndDisabled();
ImGui::SeparatorText("Snapshots");
input_int_range("Stack Size", editSettings.fileSnapshotStackSize, 0, 100);
ImGui::SetItemTooltip("Set the maximum snapshot stack size of a document (i.e., how many undo/redos are "
"preserved at a time).");
ImGui::SeparatorText(localize.get(LABEL_SNAPSHOTS));
input_int_range(localize.get(LABEL_STACK_SIZE), editSettings.fileSnapshotStackSize, 0, 100);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_STACK_SIZE));
ImGui::SeparatorText("Options");
ImGui::Checkbox("Overwrite Warning", &editSettings.fileIsWarnOverwrite);
ImGui::SetItemTooltip("A warning will be shown when saving/overwriting a file.");
ImGui::SeparatorText(localize.get(LABEL_OPTIONS));
ImGui::Checkbox(localize.get(LABEL_OVERWRITE_WARNING), &editSettings.fileIsWarnOverwrite);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_OVERWRITE_WARNING));
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Input"))
if (ImGui::BeginTabItem(localize.get(LABEL_INPUT)))
{
if (ImGui::BeginChild("##Tab Child", childSize, true))
{
ImGui::SeparatorText("Keyboard");
ImGui::SeparatorText(localize.get(LABEL_KEYBOARD));
input_float_range("Repeat Delay (seconds)", editSettings.keyboardRepeatDelay, 0.05f, 1.0f, 0.05f, 0.05f,
"%.2f");
ImGui::SetItemTooltip("Set how often, after repeating begins, key inputs will be fired.");
input_float_range(localize.get(LABEL_REPEAT_DELAY), editSettings.keyboardRepeatDelay, 0.05f, 1.0f, 0.05f,
0.05f, "%.2f");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPEAT_DELAY));
input_float_range("Repeat Rate (seconds)", editSettings.keyboardRepeatRate, 0.005f, 1.0f, 0.005f, 0.005f,
"%.3f");
ImGui::SetItemTooltip("Set how often, after repeating begins, key inputs will be fired.");
input_float_range(localize.get(LABEL_REPEAT_RATE), editSettings.keyboardRepeatRate, 0.005f, 1.0f, 0.005f,
0.005f, "%.3f");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPEAT_DELAY));
ImGui::SeparatorText("Zoom");
ImGui::SeparatorText(localize.get(LABEL_ZOOM));
input_float_range("Step", editSettings.inputZoomStep, 10.0f, 250.0f, 10.0f, 10.0f, "%.0f%%");
ImGui::SetItemTooltip("When zooming in/out with mouse or shortcut, this value will be used.");
input_float_range(localize.get(LABEL_ZOOM_STEP), editSettings.inputZoomStep, 10.0f, 250.0f, 10.0f, 10.0f,
"%.0f%%");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ZOOM_STEP));
ImGui::SeparatorText("Tool");
ImGui::SeparatorText(localize.get(LABEL_TOOL));
ImGui::Checkbox("Move Tool: Snap to Mouse", &editSettings.inputIsMoveToolSnapToMouse);
ImGui::SetItemTooltip("In Animation Preview, the Move tool will snap the frame's position right to the "
"cursor, instead of being moved at a distance.");
ImGui::Checkbox(localize.get(LABEL_MOVE_TOOL_SNAP), &editSettings.inputIsMoveToolSnapToMouse);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MOVE_TOOL_SNAP));
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Shortcuts"))
if (ImGui::BeginTabItem(localize.get(LABEL_SHORTCUTS_TAB)))
{
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
if (ImGui::BeginChild("##Tab Child", childSize, true))
{
if (ImGui::BeginTable("Shortcuts", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY))
if (ImGui::BeginTable(localize.get(LABEL_SHORTCUTS_TAB), 2,
ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("Shortcut");
ImGui::TableSetupColumn("Value");
ImGui::TableSetupColumn(localize.get(LABEL_SHORTCUT_COLUMN));
ImGui::TableSetupColumn(localize.get(LABEL_VALUE_COLUMN));
ImGui::TableHeadersRow();
for (int i = 0; i < SHORTCUT_COUNT; ++i)
@@ -465,7 +559,7 @@ namespace anm2ed::imgui
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted(SHORTCUT_STRINGS[i]);
ImGui::TextUnformatted(localize.get(::anm2ed::SHORTCUT_STRING_TYPES[i]));
ImGui::TableSetColumnIndex(1);
if (ImGui::Selectable(chordString.c_str(), isSelected)) selectedShortcut = i;
@@ -509,39 +603,41 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(3);
if (ImGui::Button("Save", widgetSize))
if (ImGui::Button(localize.get(BASIC_SAVE), widgetSize))
{
settings = editSettings;
imgui::theme_set((theme::Type)editSettings.theme);
manager.chords_set(settings);
ImGui::GetIO().KeyRepeatDelay = settings.keyboardRepeatDelay;
ImGui::GetIO().KeyRepeatRate = settings.keyboardRepeatRate;
ImGui::GetStyle().FontScaleMain = settings.uiScale;
SnapshotStack::max_size_set(settings.fileSnapshotStackSize);
imgui::theme_set((theme::Type)settings.theme);
localize.language = (Language)settings.language;
manager.chords_set(settings);
for (auto& document : manager.documents)
document.snapshots.apply_limit();
configurePopup.close();
}
ImGui::SetItemTooltip("Use the configured settings.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SETTINGS_SAVE));
ImGui::SameLine();
if (ImGui::Button("Use Default Settings", widgetSize)) editSettings = Settings();
ImGui::SetItemTooltip("Reset the settings to their defaults.");
if (ImGui::Button(localize.get(LABEL_USE_DEFAULT_SETTINGS), widgetSize)) editSettings = Settings();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_USE_DEFAULT_SETTINGS));
ImGui::SameLine();
if (ImGui::Button("Close", widgetSize)) configurePopup.close();
ImGui::SetItemTooltip("Close without updating settings.");
if (ImGui::Button(localize.get(LABEL_CLOSE), widgetSize)) configurePopup.close();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CLOSE_SETTINGS));
ImGui::EndPopup();
}
renderPopup.trigger();
if (ImGui::BeginPopupModal(renderPopup.label, &renderPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(renderPopup.label(), &renderPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto& ffmpegPath = settings.renderFFmpegPath;
auto& path = settings.renderPath;
@@ -557,59 +653,6 @@ namespace anm2ed::imgui
auto& frames = document->frames.selection;
int length = std::max(1, end - start + 1);
auto ffmpeg_is_executable = [](const std::string& pathString)
{
if (pathString.empty()) return false;
std::error_code ec{};
auto status = std::filesystem::status(pathString, ec);
if (ec || !std::filesystem::is_regular_file(status)) return false;
#ifndef _WIN32
constexpr auto EXEC_PERMS = std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec |
std::filesystem::perms::others_exec;
if ((status.permissions() & EXEC_PERMS) == std::filesystem::perms::none) return false;
#endif
return true;
};
auto png_directory_ensure = [](const std::string& directory)
{
if (directory.empty())
{
toasts.error("PNG output directory must be set.");
return false;
}
std::error_code ec{};
auto pathValue = std::filesystem::path(directory);
auto exists = std::filesystem::exists(pathValue, ec);
if (ec)
{
toasts.error(std::format("Could not access directory: {} ({})", directory, ec.message()));
return false;
}
if (exists)
{
if (!std::filesystem::is_directory(pathValue, ec) || ec)
{
toasts.error(std::format("PNG output path must be a directory: {}", directory));
return false;
}
return true;
}
if (!std::filesystem::create_directories(pathValue, ec) || ec)
{
toasts.error(std::format("Could not create directory: {} ({})", directory, ec.message()));
return false;
}
return true;
};
auto range_to_frames_set = [&]()
{
if (auto item = document->item_get())
@@ -640,11 +683,7 @@ namespace anm2ed::imgui
auto range_set = [&]()
{
if (!frames.empty())
range_to_frames_set();
else if (!isRange)
range_to_animation_set();
if (!isRange) range_to_animation_set();
length = std::max(1, end - (start + 1));
};
@@ -698,9 +737,8 @@ namespace anm2ed::imgui
if (ImGui::ImageButton("##FFmpeg Path Set", resources.icons[icon::FOLDER].id, icon_size_get()))
dialog.file_open(dialog::FFMPEG_PATH_SET);
ImGui::SameLine();
input_text_string("FFmpeg Path", &ffmpegPath);
ImGui::SetItemTooltip("Set the path where the FFmpeg installation is located.\nFFmpeg is required to render "
"animations.\nhttps://ffmpeg.org");
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 (ImGui::ImageButton("##Path Set", resources.icons[icon::FOLDER].id, icon_size_get()))
@@ -711,89 +749,89 @@ namespace anm2ed::imgui
dialog.file_save(dialogType);
}
ImGui::SameLine();
input_text_string(type == render::PNGS ? "Directory" : "Path", &path);
ImGui::SetItemTooltip("Set the output path or directory for the animation.");
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 (ImGui::Combo("Type", &type, render::STRINGS, render::COUNT)) render_set();
ImGui::SetItemTooltip("Set the type of the output.");
if (ImGui::Combo(localize.get(LABEL_TYPE), &type, render::STRINGS, render::COUNT)) render_set();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RENDER_TYPE));
if (type == render::PNGS || type == render::SPRITESHEET) ImGui::Separator();
if (type == render::PNGS)
{
if (input_text_string("Format", &format))
if (input_text_string(localize.get(LABEL_FORMAT), &format))
format = std::filesystem::path(format).replace_extension(".png").string();
ImGui::SetItemTooltip(
"For outputted images, each image will use this format.\n{} represents the index of each image.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FORMAT));
}
else if (type == render::SPRITESHEET)
{
input_int_range("Rows", rows, 1, length);
ImGui::SetItemTooltip("Set how many rows the spritesheet will have.");
input_int_range(localize.get(LABEL_GENERATE_ROWS), rows, 1, length);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROWS));
input_int_range("Columns", columns, 1, length);
ImGui::SetItemTooltip("Set how many columns the spritesheet will have.");
input_int_range(localize.get(LABEL_GENERATE_COLUMNS), columns, 1, length);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_COLUMNS));
if (ImGui::Button("Set to Recommended")) rows_columns_set();
ImGui::SetItemTooltip("Use a recommended value for rows/columns.");
if (ImGui::Button(localize.get(LABEL_SET_TO_RECOMMENDED))) rows_columns_set();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SET_TO_RECOMMENDED));
}
ImGui::Separator();
if (ImGui::Checkbox("Custom Range", &isRange))
if (ImGui::Checkbox(localize.get(LABEL_CUSTOM_RANGE), &isRange))
{
range_set();
ImGui::SetItemTooltip("Toggle using a custom range for the animation.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CUSTOM_RANGE));
}
ImGui::SameLine();
ImGui::BeginDisabled(frames.empty());
if (ImGui::Button("To Selected Frames")) range_to_frames_set();
ImGui::SetItemTooltip("If frames are selected, use that range for the rendered animation.");
if (ImGui::Button(localize.get(LABEL_TO_SELECTED_FRAMES))) range_to_frames_set();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TO_SELECTED_FRAMES));
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("To Animation Range")) range_to_animation_set();
ImGui::SetItemTooltip("Set the range to the normal range of the animation.");
if (ImGui::Button(localize.get(LABEL_TO_ANIMATION_RANGE))) range_to_animation_set();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TO_ANIMATION_RANGE));
ImGui::BeginDisabled(!isRange);
{
input_int_range("Start", start, 0, animation->frameNum);
ImGui::SetItemTooltip("Set the starting time of the animation.");
input_int_range("End", end, start, animation->frameNum);
ImGui::SetItemTooltip("Set the ending time of the animation.");
input_int_range(localize.get(LABEL_START), start, 0, animation->frameNum);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_START));
input_int_range(localize.get(LABEL_END), end, start, animation->frameNum);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_END));
}
ImGui::EndDisabled();
ImGui::Separator();
ImGui::Checkbox("Raw", &isRaw);
ImGui::SetItemTooltip("Record only the raw animation; i.e., only its layers, to its bounds.");
ImGui::Checkbox(localize.get(LABEL_RAW), &isRaw);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RAW));
ImGui::BeginDisabled(!isRaw);
{
input_float_range("Scale", scale, 1.0f, 100.0f, STEP, STEP_FAST, "%.1fx");
ImGui::SetItemTooltip("Set the output scale of the animation.");
input_float_range(localize.get(BASIC_SCALE), scale, 1.0f, 100.0f, STEP, STEP_FAST, "%.1fx");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SCALE_OUTPUT));
}
ImGui::EndDisabled();
ImGui::Separator();
ImGui::Checkbox("Sound", &settings.timelineIsSound);
ImGui::SetItemTooltip("Toggle sounds playing with triggers.\nBind sounds to events in the Events window.\nThe "
"output animation will use the played sounds.");
ImGui::Checkbox(localize.get(LABEL_SOUND), &settings.timelineIsSound);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SOUND));
ImGui::Separator();
if (ImGui::Button("Render", widgetSize))
if (ImGui::Button(localize.get(LABEL_RENDER), widgetSize))
{
bool isRender = true;
if (!ffmpeg_is_executable(ffmpegPath))
{
toasts.error("FFmpeg path must point to a valid executable file.");
toasts.push(localize.get(TOAST_INVALID_FFMPEG_PATH));
logger.error(localize.get(TOAST_INVALID_FFMPEG_PATH, anm2ed::ENGLISH));
isRender = false;
}
@@ -807,11 +845,11 @@ namespace anm2ed::imgui
renderPopup.close();
}
ImGui::SetItemTooltip("Render the animation using the current settings.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RENDER_BUTTON));
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) renderPopup.close();
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) renderPopup.close();
ImGui::EndPopup();
}
@@ -820,7 +858,7 @@ namespace anm2ed::imgui
aboutPopup.trigger();
if (ImGui::BeginPopupModal(aboutPopup.label, &aboutPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(aboutPopup.label(), &aboutPopup.isOpen, ImGuiWindowFlags_NoResize))
{
static CreditsState creditsState{};
@@ -834,14 +872,16 @@ namespace anm2ed::imgui
if (aboutPopup.isJustOpened) credits_reset();
auto size = ImGui::GetContentRegionAvail();
auto applicationLabel = localize.get(LABEL_APPLICATION_NAME);
auto versionLabel = localize.get(LABEL_APPLICATION_VERSION);
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(ANM2ED_LABEL).x) / 2);
ImGui::Text(ANM2ED_LABEL);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(applicationLabel).x) / 2);
ImGui::TextUnformatted(applicationLabel);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(VERSION_LABEL).x) / 2);
ImGui::Text(VERSION_LABEL);
ImGui::SetCursorPosX((size.x - ImGui::CalcTextSize(versionLabel).x) / 2);
ImGui::TextUnformatted(versionLabel);
ImGui::PopFont();
@@ -914,13 +954,13 @@ namespace anm2ed::imgui
overwritePopup.trigger();
if (ImGui::BeginPopupModal(overwritePopup.label, &overwritePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(overwritePopup.label(), &overwritePopup.isOpen, ImGuiWindowFlags_NoResize))
{
ImGui::Text("Are you sure? This will overwrite the existing file.");
ImGui::TextUnformatted(localize.get(LABEL_OVERWRITE_CONFIRMATION));
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Yes", widgetSize))
if (ImGui::Button(localize.get(BASIC_YES), widgetSize))
{
manager.save();
overwritePopup.close();
@@ -928,7 +968,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("No", widgetSize)) overwritePopup.close();
if (ImGui::Button(localize.get(BASIC_NO), widgetSize)) overwritePopup.close();
ImGui::EndPopup();
}
@@ -948,6 +988,3 @@ namespace anm2ed::imgui
if (shortcut(manager.chords[SHORTCUT_EXIT], shortcut::GLOBAL)) isQuitting = true;
}
}
#ifndef ANM2ED_USE_LIBXM
#define ANM2ED_USE_LIBXM 1
#endif

View File

@@ -6,6 +6,7 @@
#include "manager.h"
#include "resources.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed::imgui
{
@@ -13,11 +14,13 @@ namespace anm2ed::imgui
{
Canvas generate;
float generateTime{};
PopupHelper generatePopup{PopupHelper("Generate Animation from Grid")};
PopupHelper overwritePopup{PopupHelper("Overwrite File", imgui::POPUP_SMALL_NO_HEIGHT)};
PopupHelper renderPopup{PopupHelper("Render Animation", imgui::POPUP_SMALL_NO_HEIGHT)};
PopupHelper configurePopup{PopupHelper("Configure")};
PopupHelper aboutPopup{PopupHelper("About")};
PopupHelper generatePopup{PopupHelper(LABEL_TASKBAR_GENERATE_ANIMATION_FROM_GRID)};
PopupHelper overwritePopup{
PopupHelper(LABEL_TASKBAR_OVERWRITE_FILE, imgui::POPUP_SMALL_NO_HEIGHT)};
PopupHelper renderPopup{
PopupHelper(LABEL_TASKBAR_RENDER_ANIMATION, imgui::POPUP_SMALL_NO_HEIGHT)};
PopupHelper configurePopup{PopupHelper(LABEL_TASKBAR_CONFIGURE)};
PopupHelper aboutPopup{PopupHelper(LABEL_TASKBAR_ABOUT)};
Settings editSettings{};
int selectedShortcut{-1};
int creditsIndex{};
@@ -29,4 +32,4 @@ namespace anm2ed::imgui
Taskbar();
void update(Manager&, Settings&, Resources&, Dialog&, bool&);
};
};
};

View File

@@ -66,24 +66,7 @@ namespace anm2ed::imgui
}
}
void Toasts::info(const std::string& message)
{
toasts.emplace_back(Toast(message));
logger.info(message);
}
void Toasts::error(const std::string& message)
{
toasts.emplace_back(Toast(message));
logger.error(message);
}
void Toasts::warning(const std::string& message)
{
toasts.emplace_back(Toast(message));
logger.warning(message);
}
void Toasts::push(const std::string& message) { toasts.emplace_back(Toast(message)); }
}
anm2ed::imgui::Toasts toasts;

View File

@@ -20,9 +20,7 @@ namespace anm2ed::imgui
std::vector<Toast> toasts{};
void update();
void info(const std::string&);
void error(const std::string&);
void warning(const std::string&);
void push(const std::string&);
};
}

View File

@@ -2,6 +2,7 @@
#include <algorithm>
#include <filesystem>
#include <format>
#include <optional>
#include <ranges>
@@ -10,6 +11,7 @@
#include "imgui_.h"
#include "log.h"
#include "math_.h"
#include "strings.h"
#include "toast.h"
#include "tool.h"
#include "types.h"
@@ -69,9 +71,18 @@ namespace anm2ed::imgui
}
if (isSuccess)
toasts.info(std::format("Exported rendered frames to: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_RENDERED_FRAMES), std::make_format_args(path)));
logger.info(std::vformat(localize.get(TOAST_EXPORT_RENDERED_FRAMES, anm2ed::ENGLISH),
std::make_format_args(path)));
}
else
toasts.warning(std::format("Could not export frames to: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_RENDERED_FRAMES_FAILED),
std::make_format_args(path)));
logger.error(std::vformat(localize.get(TOAST_EXPORT_RENDERED_FRAMES_FAILED, anm2ed::ENGLISH),
std::make_format_args(path)));
}
}
else if (type == render::SPRITESHEET)
{
@@ -79,12 +90,18 @@ namespace anm2ed::imgui
auto& columns = settings.renderColumns;
if (renderFrames.empty())
toasts.warning("No frames captured for spritesheet export.");
{
toasts.push(localize.get(TOAST_SPRITESHEET_NO_FRAMES));
logger.warning(localize.get(TOAST_SPRITESHEET_NO_FRAMES, anm2ed::ENGLISH));
}
else
{
const auto& firstFrame = renderFrames.front();
auto& firstFrame = renderFrames.front();
if (firstFrame.size.x <= 0 || firstFrame.size.y <= 0 || firstFrame.pixels.empty())
toasts.warning("Spritesheet export failed: captured frames are empty.");
{
toasts.push(localize.get(TOAST_SPRITESHEET_EMPTY));
logger.error(localize.get(TOAST_SPRITESHEET_EMPTY, anm2ed::ENGLISH));
}
else
{
auto frameWidth = firstFrame.size.x;
@@ -113,18 +130,36 @@ namespace anm2ed::imgui
Texture spritesheetTexture(spritesheet.data(), spritesheetSize);
if (spritesheetTexture.write_png(path))
toasts.info(std::format("Exported spritesheet to: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_SPRITESHEET), std::make_format_args(path)));
logger.info(std::vformat(localize.get(TOAST_EXPORT_SPRITESHEET, anm2ed::ENGLISH),
std::make_format_args(path)));
}
else
toasts.warning(std::format("Could not export spritesheet to: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_SPRITESHEET_FAILED),
std::make_format_args(path)));
logger.error(std::vformat(localize.get(TOAST_EXPORT_SPRITESHEET_FAILED, anm2ed::ENGLISH),
std::make_format_args(path)));
}
}
}
}
else
{
if (animation_render(ffmpegPath, path, renderFrames, audioStream, (render::Type)type, size, anm2.info.fps))
toasts.info(std::format("Exported rendered animation to: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_RENDERED_ANIMATION), std::make_format_args(path)));
logger.info(std::vformat(localize.get(TOAST_EXPORT_RENDERED_ANIMATION, anm2ed::ENGLISH),
std::make_format_args(path)));
}
else
toasts.warning(std::format("Could not output rendered animation: {}", path));
{
toasts.push(std::vformat(localize.get(TOAST_EXPORT_RENDERED_ANIMATION_FAILED),
std::make_format_args(path)));
logger.error(std::vformat(localize.get(TOAST_EXPORT_RENDERED_ANIMATION_FAILED, anm2ed::ENGLISH),
std::make_format_args(path)));
}
}
renderFrames.clear();
@@ -242,24 +277,24 @@ namespace anm2ed::imgui
auto center_view = [&]() { pan = vec2(); };
if (ImGui::Begin("Animation Preview", &settings.windowIsAnimationPreview))
if (ImGui::Begin(localize.get(LABEL_ANIMATION_PREVIEW_WINDOW), &settings.windowIsAnimationPreview))
{
auto childSize = ImVec2(row_widget_width_get(4),
(ImGui::GetTextLineHeightWithSpacing() * 4) + (ImGui::GetStyle().WindowPadding.y * 2));
if (ImGui::BeginChild("##Grid Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::Checkbox("Grid", &isGrid);
ImGui::SetItemTooltip("Toggle the visibility of the grid.");
ImGui::Checkbox(localize.get(BASIC_GRID), &isGrid);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_VISIBILITY));
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the grid's color.");
ImGui::ColorEdit4(localize.get(BASIC_COLOR), value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_COLOR));
input_int2_range("Size", gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("Change the size of all cells in the grid.");
input_int2_range(localize.get(BASIC_SIZE), gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_SIZE));
input_int2_range("Offset", gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("Change the offset of the grid.");
input_int2_range(localize.get(BASIC_OFFSET), gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_OFFSET));
}
ImGui::EndChild();
@@ -267,23 +302,26 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##View Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::InputFloat("Zoom", &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("Change the zoom of the preview.");
ImGui::InputFloat(localize.get(BASIC_ZOOM), &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PREVIEW_ZOOM));
auto widgetSize = widget_size_with_row_get(2);
shortcut(manager.chords[SHORTCUT_CENTER_VIEW]);
if (ImGui::Button("Center View", widgetSize)) pan = vec2();
set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
if (ImGui::Button(localize.get(LABEL_CENTER_VIEW), widgetSize)) pan = vec2();
set_item_tooltip_shortcut(localize.get(TOOLTIP_CENTER_VIEW), settings.shortcutCenterView);
ImGui::SameLine();
shortcut(manager.chords[SHORTCUT_FIT]);
if (ImGui::Button("Fit", widgetSize))
if (ImGui::Button(localize.get(LABEL_FIT), widgetSize))
if (animation) set_to_rect(zoom, pan, animation->rect(isRootTransform));
set_item_tooltip_shortcut("Set the view to match the extent of the animation.", settings.shortcutFit);
set_item_tooltip_shortcut(localize.get(TOOLTIP_FIT), settings.shortcutFit);
ImGui::TextUnformatted(std::format(POSITION_FORMAT, (int)mousePos.x, (int)mousePos.y).c_str());
auto mousePosInt = ivec2(mousePos);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_POSITION_SPACED), std::make_format_args(mousePosInt.x, mousePosInt.y))
.c_str());
}
ImGui::EndChild();
@@ -291,20 +329,21 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Background Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::ColorEdit3("Background", value_ptr(backgroundColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the background color.");
ImGui::ColorEdit3(localize.get(LABEL_BACKGROUND_COLOR), value_ptr(backgroundColor),
ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BACKGROUND_COLOR));
ImGui::SameLine();
ImGui::Checkbox("Axes", &isAxes);
ImGui::SetItemTooltip("Toggle the axes' visbility.");
ImGui::Checkbox(localize.get(LABEL_AXES), &isAxes);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_AXES));
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(axesColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Set the color of the axes.");
ImGui::ColorEdit4(localize.get(BASIC_COLOR), value_ptr(axesColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_AXES_COLOR));
combo_negative_one_indexed("Overlay", &overlayIndex, document.animation.labels);
ImGui::SetItemTooltip("Set an animation to be drawn over the current animation.");
combo_negative_one_indexed(localize.get(LABEL_OVERLAY), &overlayIndex, document.animation.labels);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_OVERLAY));
ImGui::InputFloat("Alpha", &overlayTransparency, 0, 0, "%.0f");
ImGui::SetItemTooltip("Set the alpha of the overlayed animation.");
ImGui::InputFloat(localize.get(BASIC_ALPHA), &overlayTransparency, 0, 0, "%.0f");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_OVERLAY_ALPHA));
}
ImGui::EndChild();
@@ -316,10 +355,10 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Helpers Child 1", helpersChildSize))
{
ImGui::Checkbox("Root Transform", &isRootTransform);
ImGui::SetItemTooltip("Root frames will transform the rest of the animation.");
ImGui::Checkbox("Pivots", &isPivots);
ImGui::SetItemTooltip("Toggle the visibility of the animation's pivots.");
ImGui::Checkbox(localize.get(LABEL_ROOT_TRANSFORM), &isRootTransform);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROOT_TRANSFORM));
ImGui::Checkbox(localize.get(LABEL_PIVOTS), &isPivots);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PIVOTS));
}
ImGui::EndChild();
@@ -327,10 +366,10 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Helpers Child 2", helpersChildSize))
{
ImGui::Checkbox("Alt Icons", &isAltIcons);
ImGui::SetItemTooltip("Toggle a different appearance of the target icons.");
ImGui::Checkbox("Border", &isBorder);
ImGui::SetItemTooltip("Toggle the visibility of borders around layers.");
ImGui::Checkbox(localize.get(LABEL_ALT_ICONS), &isAltIcons);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ALT_ICONS));
ImGui::Checkbox(localize.get(LABEL_BORDER), &isBorder);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BORDER));
}
ImGui::EndChild();
}
@@ -592,12 +631,11 @@ namespace anm2ed::imgui
{
auto layeredOnions = settings.onionskinIsEnabled ? &onionskinSamples : nullptr;
render(animation, frameTime, {}, 0.0f, layeredOnions,
settings.onionskinMode == static_cast<int>(OnionskinMode::INDEX));
render(animation, frameTime, {}, 0.0f, layeredOnions, settings.onionskinMode == (int)OnionskinMode::INDEX);
if (auto overlayAnimation = anm2.animation_get(overlayIndex))
render(overlayAnimation, frameTime, {}, 1.0f - math::uint8_to_float(overlayTransparency), layeredOnions,
settings.onionskinMode == static_cast<int>(OnionskinMode::INDEX));
settings.onionskinMode == (int)OnionskinMode::INDEX);
}
unbind();
@@ -692,7 +730,7 @@ namespace anm2ed::imgui
if (!frame) break;
if (isBegin)
{
document.snapshot("Frame Position");
document.snapshot(localize.get(EDIT_FRAME_POSITION));
if (isMouseClicked)
{
moveOffset = settings.inputIsMoveToolSnapToMouse ? vec2() : mousePos - frame->position;
@@ -710,16 +748,16 @@ namespace anm2ed::imgui
{
if (ImGui::BeginTooltip())
{
auto positionFormat = math::vec2_format_get(frame->position);
auto positionString = std::format("Position: ({}, {})", positionFormat, positionFormat);
ImGui::Text(positionString.c_str(), frame->position.x, frame->position.y);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_POSITION),
std::make_format_args(frame->position.x, frame->position.y))
.c_str());
ImGui::EndTooltip();
}
}
break;
case tool::SCALE:
if (!frame) break;
if (isBegin) document.snapshot("Frame Scale");
if (isBegin) document.snapshot(localize.get(EDIT_FRAME_SCALE));
if (isMouseDown)
{
frame->scale += vec2(mouseDelta.x, mouseDelta.y);
@@ -734,9 +772,9 @@ namespace anm2ed::imgui
{
if (ImGui::BeginTooltip())
{
auto scaleFormat = math::vec2_format_get(frame->scale);
auto scaleString = std::format("Scale: ({}, {})", scaleFormat, scaleFormat);
ImGui::Text(scaleString.c_str(), frame->scale.x, frame->scale.y);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SCALE), std::make_format_args(frame->scale.x, frame->scale.y))
.c_str());
ImGui::EndTooltip();
}
}
@@ -745,7 +783,7 @@ namespace anm2ed::imgui
break;
case tool::ROTATE:
if (!frame) break;
if (isBegin) document.snapshot("Frame Rotation");
if (isBegin) document.snapshot(localize.get(EDIT_FRAME_ROTATION));
if (isMouseDown) frame->rotation += mouseDelta.y;
if (isLeftPressed || isDownPressed) frame->rotation -= step;
if (isUpPressed || isRightPressed) frame->rotation += step;
@@ -754,9 +792,8 @@ namespace anm2ed::imgui
{
if (ImGui::BeginTooltip())
{
auto rotationFormat = math::float_format_get(frame->rotation);
auto rotationString = std::format("Rotation: {}", rotationFormat);
ImGui::Text(rotationString.c_str(), frame->rotation);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_ROTATION), std::make_format_args(frame->rotation)).c_str());
ImGui::EndTooltip();
}
}
@@ -779,7 +816,7 @@ namespace anm2ed::imgui
manager.progressPopup.trigger();
if (ImGui::BeginPopupModal(manager.progressPopup.label, &manager.progressPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(manager.progressPopup.label(), &manager.progressPopup.isOpen, ImGuiWindowFlags_NoResize))
{
if (!animation) return;
@@ -789,9 +826,9 @@ namespace anm2ed::imgui
ImGui::ProgressBar(progress);
ImGui::Text("Once recording is complete, rendering may take some time.\nPlease be patient...");
ImGui::TextUnformatted(localize.get(TEXT_RECORDING_PROGRESS));
if (ImGui::Button("Cancel", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
if (ImGui::Button(localize.get(BASIC_CANCEL), ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
renderFrames.clear();

View File

@@ -1,8 +1,11 @@
#include "animations.h"
#include <cstddef>
#include <format>
#include <ranges>
#include "log.h"
#include "strings.h"
#include "toast.h"
#include "vector_.h"
@@ -40,7 +43,7 @@ namespace anm2ed::imgui
}
};
if (ImGui::Begin("Animations", &settings.windowIsAnimations))
if (ImGui::Begin(localize.get(LABEL_ANIMATIONS_WINDOW), &settings.windowIsAnimations))
{
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyPressed(ImGuiKey_Escape))
reference = {};
@@ -75,7 +78,7 @@ namespace anm2ed::imgui
document.frames.clear();
if (renameState == RENAME_BEGIN)
document.snapshot("Rename Animation");
document.snapshot(localize.get(SNAPSHOT_RENAME_ANIMATION));
else if (renameState == RENAME_FINISHED)
{
if (anm2.animations.items.size() == 1) anm2.animations.defaultAnimation = animation.name;
@@ -97,10 +100,17 @@ namespace anm2ed::imgui
ImGui::TextUnformatted(animation.name.c_str());
ImGui::PopFont();
if (isDefault) ImGui::TextUnformatted("(Default Animation)");
if (isDefault)
{
ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
ImGui::TextUnformatted(localize.get(BASIC_DEFAULT));
ImGui::PopFont();
}
ImGui::Text("Length: %d", animation.frameNum);
ImGui::Text("Loop: %s", animation.isLoop ? "true" : "false");
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_LENGTH), std::make_format_args(animation.frameNum)).c_str());
auto loopLabel = animation.isLoop ? localize.get(BASIC_YES) : localize.get(BASIC_NO);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_LOOP), std::make_format_args(loopLabel)).c_str());
ImGui::EndTooltip();
}
@@ -124,7 +134,7 @@ namespace anm2ed::imgui
auto payloadCount = payload->DataSize / sizeof(int);
std::vector<int> indices(payloadIndices, payloadIndices + payloadCount);
std::sort(indices.begin(), indices.end());
DOCUMENT_EDIT(document, "Move Animation(s)", Document::ANIMATIONS,
DOCUMENT_EDIT(document, localize.get(EDIT_MOVE_ANIMATIONS), Document::ANIMATIONS,
selection = vector::move_indices(anm2.animations.items, indices, i));
}
ImGui::EndDragDropTarget();
@@ -151,7 +161,7 @@ namespace anm2ed::imgui
auto cut = [&]()
{
copy();
DOCUMENT_EDIT(document, "Cut Animation(s)", Document::ANIMATIONS, animations_remove());
DOCUMENT_EDIT(document, localize.get(EDIT_CUT_ANIMATIONS), Document::ANIMATIONS, animations_remove());
};
auto paste = [&]()
@@ -163,13 +173,18 @@ namespace anm2ed::imgui
auto start = selection.empty() ? anm2.animations.items.size() : *selection.rbegin() + 1;
std::set<int> indices{};
std::string errorString{};
if (anm2.animations_deserialize(clipboardText, start, indices, &errorString))
selection = indices;
else
toasts.error(std::format("Failed to deserialize animation(s): {}", errorString));
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, "Paste Animation(s)", Document::ANIMATIONS, deserialize());
DOCUMENT_EDIT(document, localize.get(EDIT_PASTE_ANIMATIONS), Document::ANIMATIONS, deserialize());
};
if (shortcut(manager.chords[SHORTCUT_CUT], shortcut::FOCUSED)) cut();
@@ -178,9 +193,20 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
if (ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, !selection.empty() || hovered > -1)) cut();
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
if (ImGui::MenuItem("Paste", settings.shortcutPaste.c_str(), false, !clipboard.is_empty())) paste();
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))
{
copy();
}
if (ImGui::MenuItem(localize.get(BASIC_PASTE), settings.shortcutPaste.c_str(), false, !clipboard.is_empty()))
{
paste();
}
ImGui::EndPopup();
}
}
@@ -189,7 +215,7 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(5);
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize))
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
{
auto add = [&]()
{
@@ -219,16 +245,16 @@ namespace anm2ed::imgui
newAnimationSelectedIndex = index;
};
DOCUMENT_EDIT(document, "Add Animation", Document::ANIMATIONS, add());
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_ANIMATION), Document::ANIMATIONS, add());
}
set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd);
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("Duplicate", widgetSize))
if (ImGui::Button(localize.get(BASIC_DUPLICATE), widgetSize))
{
auto duplicate = [&]()
{
@@ -242,9 +268,9 @@ namespace anm2ed::imgui
}
};
DOCUMENT_EDIT(document, "Duplicate Animation(s)", Document::ANIMATIONS, duplicate());
DOCUMENT_EDIT(document, localize.get(EDIT_DUPLICATE_ANIMATIONS), Document::ANIMATIONS, duplicate());
}
set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate);
set_item_tooltip_shortcut(localize.get(TOOLTIP_DUPLICATE_ANIMATION), settings.shortcutDuplicate);
ImGui::SameLine();
@@ -274,12 +300,12 @@ namespace anm2ed::imgui
reference = {merged};
};
DOCUMENT_EDIT(document, "Merge Animations", Document::ANIMATIONS, merge_quick())
DOCUMENT_EDIT(document, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, merge_quick())
}
ImGui::BeginDisabled(selection.size() != 1);
{
if (ImGui::Button("Merge", widgetSize))
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize))
{
mergePopup.open();
mergeSelection.clear();
@@ -287,34 +313,32 @@ namespace anm2ed::imgui
}
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Open the merge popup.\nUsing the shortcut will merge the animations with\nthe last "
"configured merge settings.",
settings.shortcutMerge);
set_item_tooltip_shortcut(localize.get(TOOLTIP_OPEN_MERGE_POPUP), settings.shortcutMerge);
ImGui::SameLine();
shortcut(manager.chords[SHORTCUT_REMOVE]);
if (ImGui::Button("Remove", widgetSize))
DOCUMENT_EDIT(document, "Remove Animation(s)", Document::ANIMATIONS, animations_remove());
set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutRemove);
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("Default", widgetSize))
if (ImGui::Button(localize.get(BASIC_DEFAULT), widgetSize))
{
DOCUMENT_EDIT(document, "Default Animation", Document::ANIMATIONS,
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("Set the selected animation as the default.", settings.shortcutDefault);
set_item_tooltip_shortcut(localize.get(TOOLTIP_SET_DEFAULT_ANIMATION), settings.shortcutDefault);
}
ImGui::EndDisabled();
mergePopup.trigger();
if (ImGui::BeginPopupModal(mergePopup.label, &mergePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(mergePopup.label(), &mergePopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto merge_close = [&]()
{
@@ -332,7 +356,7 @@ namespace anm2ed::imgui
ImVec2(0, ImGui::GetContentRegionAvail().y -
(optionsSize.y + deleteAfterSize.y + footerSize.y + ImGui::GetStyle().ItemSpacing.y * 3));
if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild(localize.get(LABEL_ANIMATIONS_CHILD), animationsSize, ImGuiChildFlags_Borders))
{
mergeSelection.start(anm2.animations.items.size());
@@ -354,37 +378,37 @@ namespace anm2ed::imgui
}
ImGui::EndChild();
if (ImGui::BeginChild("Merge Options", optionsSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Merge Options", optionsSize, ImGuiChildFlags_Borders))
{
auto size = ImVec2(optionsSize.x * 0.5f, optionsSize.y - ImGui::GetStyle().WindowPadding.y * 2);
if (ImGui::BeginChild("Merge Options 1", size))
if (ImGui::BeginChild("##Merge Options 1", size))
{
ImGui::RadioButton("Append Frames", &type, merge::APPEND);
ImGui::RadioButton("Prepend Frames", &type, merge::PREPEND);
ImGui::RadioButton(localize.get(LABEL_APPEND_FRAMES), &type, merge::APPEND);
ImGui::RadioButton(localize.get(LABEL_PREPEND_FRAMES), &type, merge::PREPEND);
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Merge Options 2", size))
if (ImGui::BeginChild("##Merge Options 2", size))
{
ImGui::RadioButton("Replace Frames", &type, merge::REPLACE);
ImGui::RadioButton("Ignore Frames", &type, merge::IGNORE);
ImGui::RadioButton(localize.get(LABEL_REPLACE_FRAMES), &type, merge::REPLACE);
ImGui::RadioButton(localize.get(LABEL_IGNORE_FRAMES), &type, merge::IGNORE);
}
ImGui::EndChild();
}
ImGui::EndChild();
if (ImGui::BeginChild("Merge Delete After", deleteAfterSize, ImGuiChildFlags_Borders))
ImGui::Checkbox("Delete Animations After", &isDeleteAnimationsAfter);
if (ImGui::BeginChild("##Merge Delete After", deleteAfterSize, ImGuiChildFlags_Borders))
ImGui::Checkbox(localize.get(LABEL_DELETE_ANIMATIONS_AFTER), &isDeleteAnimationsAfter);
ImGui::EndChild();
auto widgetSize = widget_size_with_row_get(2);
ImGui::BeginDisabled(mergeSelection.empty());
{
if (ImGui::Button("Merge", widgetSize))
if (ImGui::Button(localize.get(LABEL_MERGE), widgetSize))
{
auto merge = [&]()
{
@@ -396,13 +420,13 @@ namespace anm2ed::imgui
reference = {merged};
};
DOCUMENT_EDIT(document, "Merge Animations", Document::ANIMATIONS, merge());
DOCUMENT_EDIT(document, localize.get(EDIT_MERGE_ANIMATIONS), Document::ANIMATIONS, merge());
merge_close();
}
}
ImGui::EndDisabled();
ImGui::SameLine();
if (ImGui::Button("Close", widgetSize)) merge_close();
if (ImGui::Button(localize.get(LABEL_CLOSE), widgetSize)) merge_close();
ImGui::EndPopup();
}

View File

@@ -4,12 +4,13 @@
#include "manager.h"
#include "resources.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed::imgui
{
class Animations
{
PopupHelper mergePopup{PopupHelper("Merge Animations")};
PopupHelper mergePopup{PopupHelper(LABEL_ANIMATIONS_MERGE_POPUP)};
int newAnimationSelectedIndex{-1};
RenameState renameState{RENAME_SELECTABLE};

View File

@@ -2,7 +2,9 @@
#include <ranges>
#include "log.h"
#include "map_.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::util;
@@ -22,7 +24,7 @@ namespace anm2ed::imgui
hovered = -1;
if (ImGui::Begin("Events", &settings.windowIsEvents))
if (ImGui::Begin(localize.get(LABEL_EVENTS_WINDOW), &settings.windowIsEvents))
{
auto childSize = size_without_footer_get();
@@ -41,7 +43,7 @@ namespace anm2ed::imgui
event.name, selection.contains(id), ImGuiSelectableFlags_None, renameState))
{
if (renameState == RENAME_BEGIN)
document.snapshot("Rename Event");
document.snapshot(localize.get(EDIT_RENAME_EVENT));
else if (renameState == RENAME_FINISHED)
document.change(Document::EVENTS);
}
@@ -58,6 +60,7 @@ namespace anm2ed::imgui
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(event.name.c_str());
ImGui::PopFont();
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
ImGui::EndTooltip();
}
ImGui::PopID();
@@ -81,11 +84,16 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
std::string errorString{};
document.snapshot("Paste Event(s)");
document.snapshot(localize.get(EDIT_PASTE_EVENTS));
if (anm2.events_deserialize(clipboard.get(), type, &errorString))
document.change(Document::EVENTS);
else
toasts.error(std::format("Failed to deserialize event(s): {}", errorString));
{
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_COPY], shortcut::FOCUSED)) copy();
@@ -93,13 +101,15 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
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))
copy();
if (ImGui::BeginMenu("Paste", !clipboard.is_empty()))
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
{
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
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();
}
@@ -112,7 +122,7 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize))
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
{
auto add = [&]()
{
@@ -123,14 +133,14 @@ namespace anm2ed::imgui
newEventId = id;
};
DOCUMENT_EDIT(document, "Add Event", Document::EVENTS, add());
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_EVENT), Document::EVENTS, add());
}
set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
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("Remove Unused", widgetSize))
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
{
auto remove_unused = [&]()
{
@@ -139,11 +149,10 @@ namespace anm2ed::imgui
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Events", Document::EVENTS, remove_unused());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_EVENTS), Document::EVENTS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)",
settings.shortcutRemove);
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_EVENTS), settings.shortcutRemove);
}
ImGui::End();
}

View File

@@ -3,6 +3,7 @@
#include <glm/gtc/type_ptr.hpp>
#include "math_.h"
#include "strings.h"
#include "types.h"
using namespace anm2ed::util::math;
@@ -13,7 +14,7 @@ namespace anm2ed::imgui
{
void FrameProperties::update(Manager& manager, Settings& settings)
{
if (ImGui::Begin("Frame Properties", &settings.windowIsFrameProperties))
if (ImGui::Begin(localize.get(LABEL_FRAME_PROPERTIES_WINDOW), &settings.windowIsFrameProperties))
{
auto& document = *manager.get();
auto& frames = document.frames.selection;
@@ -28,103 +29,117 @@ namespace anm2ed::imgui
{
if (type == anm2::TRIGGER)
{
if (combo_negative_one_indexed("Event", frame ? &useFrame.eventID : &dummy_value_negative<int>(),
if (combo_negative_one_indexed(localize.get(BASIC_EVENT),
frame ? &useFrame.eventID : &dummy_value_negative<int>(),
document.event.labels))
DOCUMENT_EDIT(document, "Trigger Event", Document::FRAMES, frame->eventID = useFrame.eventID);
ImGui::SetItemTooltip("Change the event this trigger uses.");
DOCUMENT_EDIT(document, localize.get(EDIT_TRIGGER_EVENT), Document::FRAMES,
frame->eventID = useFrame.eventID);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRIGGER_EVENT));
if (combo_negative_one_indexed("Sound", frame ? &useFrame.soundID : &dummy_value_negative<int>(),
if (combo_negative_one_indexed(localize.get(BASIC_SOUND),
frame ? &useFrame.soundID : &dummy_value_negative<int>(),
document.sound.labels))
DOCUMENT_EDIT(document, "Trigger Sound", Document::FRAMES, frame->soundID = useFrame.soundID);
ImGui::SetItemTooltip("Change the sound this trigger uses.");
DOCUMENT_EDIT(document, localize.get(EDIT_TRIGGER_SOUND), Document::FRAMES,
frame->soundID = useFrame.soundID);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRIGGER_SOUND));
if (ImGui::InputInt("At Frame", frame ? &useFrame.atFrame : &dummy_value<int>(), STEP, STEP_FAST,
!frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, "Trigger At Frame", Document::FRAMES, frame->atFrame = useFrame.atFrame);
ImGui::SetItemTooltip("Change the frame the trigger will be activated at.");
if (ImGui::InputInt(localize.get(BASIC_AT_FRAME), frame ? &useFrame.atFrame : &dummy_value<int>(), STEP,
STEP_FAST, !frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, localize.get(EDIT_TRIGGER_AT_FRAME), Document::FRAMES,
frame->atFrame = useFrame.atFrame);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRIGGER_AT_FRAME));
if (ImGui::Checkbox("Visible", frame ? &useFrame.isVisible : &dummy_value<bool>()))
DOCUMENT_EDIT(document, "Trigger Visibility", Document::FRAMES, frame->isVisible = useFrame.isVisible);
ImGui::SetItemTooltip("Toggle the trigger's visibility.");
if (ImGui::Checkbox(localize.get(BASIC_VISIBLE), frame ? &useFrame.isVisible : &dummy_value<bool>()))
DOCUMENT_EDIT(document, localize.get(EDIT_TRIGGER_VISIBILITY), Document::FRAMES,
frame->isVisible = useFrame.isVisible);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRIGGER_VISIBILITY));
}
else
{
ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_);
{
if (ImGui::InputFloat2("Crop", frame ? value_ptr(useFrame.crop) : &dummy_value<float>(),
if (ImGui::InputFloat2(localize.get(BASIC_CROP), frame ? value_ptr(useFrame.crop) : &dummy_value<float>(),
frame ? vec2_format_get(useFrame.crop) : ""))
DOCUMENT_EDIT(document, "Frame Crop", Document::FRAMES, frame->crop = useFrame.crop);
ImGui::SetItemTooltip("Change the crop position the frame uses.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_CROP), Document::FRAMES, frame->crop = useFrame.crop);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CROP));
if (ImGui::InputFloat2("Size", frame ? value_ptr(useFrame.size) : &dummy_value<float>(),
if (ImGui::InputFloat2(localize.get(BASIC_SIZE), frame ? value_ptr(useFrame.size) : &dummy_value<float>(),
frame ? vec2_format_get(useFrame.size) : ""))
DOCUMENT_EDIT(document, "Frame Size", Document::FRAMES, frame->size = useFrame.size);
ImGui::SetItemTooltip("Change the size of the crop the frame uses.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_SIZE), Document::FRAMES, frame->size = useFrame.size);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SIZE));
}
ImGui::EndDisabled();
if (ImGui::InputFloat2("Position", frame ? value_ptr(useFrame.position) : &dummy_value<float>(),
if (ImGui::InputFloat2(localize.get(BASIC_POSITION),
frame ? value_ptr(useFrame.position) : &dummy_value<float>(),
frame ? vec2_format_get(useFrame.position) : ""))
DOCUMENT_EDIT(document, "Frame Position", Document::FRAMES, frame->position = useFrame.position);
ImGui::SetItemTooltip("Change the position of the frame.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_POSITION), Document::FRAMES,
frame->position = useFrame.position);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_POSITION));
ImGui::BeginDisabled(type == anm2::ROOT || type == anm2::NULL_);
{
if (ImGui::InputFloat2("Pivot", frame ? value_ptr(useFrame.pivot) : &dummy_value<float>(),
if (ImGui::InputFloat2(localize.get(BASIC_PIVOT),
frame ? value_ptr(useFrame.pivot) : &dummy_value<float>(),
frame ? vec2_format_get(useFrame.pivot) : ""))
DOCUMENT_EDIT(document, "Frame Pivot", Document::FRAMES, frame->pivot = useFrame.pivot);
ImGui::SetItemTooltip("Change the pivot of the frame; i.e., where it is centered.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_PIVOT), Document::FRAMES,
frame->pivot = useFrame.pivot);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_PIVOT));
}
ImGui::EndDisabled();
if (ImGui::InputFloat2("Scale", frame ? value_ptr(useFrame.scale) : &dummy_value<float>(),
if (ImGui::InputFloat2(localize.get(BASIC_SCALE), frame ? value_ptr(useFrame.scale) : &dummy_value<float>(),
frame ? vec2_format_get(useFrame.scale) : ""))
DOCUMENT_EDIT(document, "Frame Scale", Document::FRAMES, frame->scale = useFrame.scale);
ImGui::SetItemTooltip("Change the scale of the frame, in percent.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_SCALE), Document::FRAMES, frame->scale = useFrame.scale);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SCALE));
if (ImGui::InputFloat("Rotation", frame ? &useFrame.rotation : &dummy_value<float>(), STEP, STEP_FAST,
frame ? float_format_get(useFrame.rotation) : ""))
DOCUMENT_EDIT(document, "Frame Rotation", Document::FRAMES, frame->rotation = useFrame.rotation);
ImGui::SetItemTooltip("Change the rotation of the frame.");
if (ImGui::InputFloat(localize.get(BASIC_ROTATION), frame ? &useFrame.rotation : &dummy_value<float>(),
STEP, STEP_FAST, frame ? float_format_get(useFrame.rotation) : ""))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_ROTATION), Document::FRAMES,
frame->rotation = useFrame.rotation);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROTATION));
if (input_int_range("Duration", frame ? useFrame.duration : dummy_value<int>(),
if (input_int_range(localize.get(BASIC_DURATION), frame ? useFrame.duration : dummy_value<int>(),
frame ? anm2::FRAME_DURATION_MIN : 0, anm2::FRAME_DURATION_MAX, STEP, STEP_FAST,
!frame ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, "Frame Duration", Document::FRAMES, frame->duration = useFrame.duration);
ImGui::SetItemTooltip("Change how long the frame lasts.");
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_DURATION), Document::FRAMES,
frame->duration = useFrame.duration);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_DURATION));
if (ImGui::ColorEdit4("Tint", frame ? value_ptr(useFrame.tint) : &dummy_value<float>()))
DOCUMENT_EDIT(document, "Frame Tint", Document::FRAMES, frame->tint = useFrame.tint);
ImGui::SetItemTooltip("Change the tint of the frame.");
if (ImGui::ColorEdit4(localize.get(BASIC_TINT), frame ? value_ptr(useFrame.tint) : &dummy_value<float>()))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_TINT), Document::FRAMES, frame->tint = useFrame.tint);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TINT));
if (ImGui::ColorEdit3("Color Offset", frame ? value_ptr(useFrame.colorOffset) : &dummy_value<float>()))
DOCUMENT_EDIT(document, "Frame Color Offset", Document::FRAMES,
if (ImGui::ColorEdit3(localize.get(BASIC_COLOR_OFFSET),
frame ? value_ptr(useFrame.colorOffset) : &dummy_value<float>()))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_COLOR_OFFSET), Document::FRAMES,
frame->colorOffset = useFrame.colorOffset);
ImGui::SetItemTooltip("Change the color added onto the frame.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_COLOR_OFFSET));
if (ImGui::Checkbox("Visible", frame ? &useFrame.isVisible : &dummy_value<bool>()))
DOCUMENT_EDIT(document, "Frame Visibility", Document::FRAMES, frame->isVisible = useFrame.isVisible);
ImGui::SetItemTooltip("Toggle the frame's visibility.");
if (ImGui::Checkbox(localize.get(BASIC_VISIBLE), frame ? &useFrame.isVisible : &dummy_value<bool>()))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_VISIBILITY), Document::FRAMES,
frame->isVisible = useFrame.isVisible);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FRAME_VISIBILITY));
ImGui::SameLine();
if (ImGui::Checkbox("Interpolated", frame ? &useFrame.isInterpolated : &dummy_value<bool>()))
DOCUMENT_EDIT(document, "Frame Interpolation", Document::FRAMES,
if (ImGui::Checkbox(localize.get(BASIC_INTERPOLATED),
frame ? &useFrame.isInterpolated : &dummy_value<bool>()))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_INTERPOLATION), Document::FRAMES,
frame->isInterpolated = useFrame.isInterpolated);
ImGui::SetItemTooltip(
"Toggle the frame interpolating; i.e., blending its values into the next frame based on the time.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FRAME_INTERPOLATION));
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Flip X", widgetSize))
DOCUMENT_EDIT(document, "Frame Flip X", Document::FRAMES, frame->scale.x = -frame->scale.x);
ImGui::SetItemTooltip("%s", "Flip the horizontal scale of the frame, to cheat mirroring the frame "
"horizontally.\n(Note: the format does not support mirroring.)");
if (ImGui::Button(localize.get(LABEL_FLIP_X), widgetSize))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_FLIP_X), Document::FRAMES,
frame->scale.x = -frame->scale.x);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FLIP_X));
ImGui::SameLine();
if (ImGui::Button("Flip Y", widgetSize))
DOCUMENT_EDIT(document, "Frame Flip Y", Document::FRAMES, frame->scale.y = -frame->scale.y);
ImGui::SetItemTooltip("%s", "Flip the vertical scale of the frame, to cheat mirroring the frame "
"vertically.\n(Note: the format does not support mirroring.)");
if (ImGui::Button(localize.get(LABEL_FLIP_Y), widgetSize))
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_FLIP_Y), Document::FRAMES,
frame->scale.y = -frame->scale.y);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FLIP_Y));
}
}
ImGui::EndDisabled();
@@ -184,18 +199,18 @@ namespace anm2ed::imgui
#undef PROPERTIES_WIDGET
float2_value("##Is Crop", "Crop", isCrop, crop);
float2_value("##Is Size", "Size", isSize, size);
float2_value("##Is Position", "Position", isPosition, position);
float2_value("##Is Pivot", "Pivot", isPivot, pivot);
float2_value("##Is Scale", "Scale", isScale, scale);
float_value("##Is Rotation", "Rotation", isRotation, rotation);
duration_value("##Is Duration", "Duration", isDuration, duration);
color4_value("##Is Tint", "Tint", isTint, tint);
color3_value("##Is Color Offset", "Color Offset", isColorOffset, colorOffset);
bool_value("##Is Visible", "Visible", isVisibleSet, isVisible);
float2_value("##Is Crop", localize.get(BASIC_CROP), isCrop, crop);
float2_value("##Is Size", localize.get(BASIC_SIZE), isSize, size);
float2_value("##Is Position", localize.get(BASIC_POSITION), isPosition, position);
float2_value("##Is Pivot", localize.get(BASIC_PIVOT), isPivot, pivot);
float2_value("##Is Scale", localize.get(BASIC_SCALE), isScale, scale);
float_value("##Is Rotation", localize.get(BASIC_ROTATION), isRotation, rotation);
duration_value("##Is Duration", localize.get(BASIC_DURATION), isDuration, duration);
color4_value("##Is Tint", localize.get(BASIC_TINT), isTint, tint);
color3_value("##Is Color Offset", localize.get(BASIC_COLOR_OFFSET), isColorOffset, colorOffset);
bool_value("##Is Visible", localize.get(BASIC_VISIBLE), isVisibleSet, isVisible);
ImGui::SameLine();
bool_value("##Is Interpolated", "Interpolated", isInterpolatedSet, isInterpolated);
bool_value("##Is Interpolated", localize.get(BASIC_INTERPOLATED), isInterpolatedSet, isInterpolated);
auto frame_change = [&](anm2::ChangeType type)
{
@@ -212,30 +227,30 @@ namespace anm2ed::imgui
if (isVisibleSet) frameChange.isVisible = std::make_optional(isVisible);
if (isInterpolatedSet) frameChange.isInterpolated = std::make_optional(isInterpolated);
DOCUMENT_EDIT(document, "Change Frame Properties", Document::FRAMES,
DOCUMENT_EDIT(document, localize.get(EDIT_CHANGE_FRAME_PROPERTIES), Document::FRAMES,
document.item_get()->frames_change(frameChange, type, *frames.begin(), (int)frames.size()));
};
auto rowOneWidgetSize = widget_size_with_row_get(1);
if (ImGui::Button("Adjust", rowOneWidgetSize)) frame_change(anm2::ADJUST);
ImGui::SetItemTooltip("Set the value of each specified value onto the frame's equivalent.");
if (ImGui::Button(localize.get(LABEL_ADJUST), rowOneWidgetSize)) frame_change(anm2::ADJUST);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ADJUST));
auto rowTwoWidgetSize = widget_size_with_row_get(4);
if (ImGui::Button("Add", rowTwoWidgetSize)) frame_change(anm2::ADD);
ImGui::SetItemTooltip("Add the specified values onto each frame.\n(Boolean values will simply be set.)");
if (ImGui::Button(localize.get(BASIC_ADD), rowTwoWidgetSize)) frame_change(anm2::ADD);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ADD_VALUES));
ImGui::SameLine();
if (ImGui::Button("Subtract", rowTwoWidgetSize)) frame_change(anm2::SUBTRACT);
ImGui::SetItemTooltip("Subtract the specified values from each frame.\n(Boolean values will simply be set.)");
if (ImGui::Button(localize.get(LABEL_SUBTRACT), rowTwoWidgetSize)) frame_change(anm2::SUBTRACT);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SUBTRACT_VALUES));
ImGui::SameLine();
if (ImGui::Button("Multiply", rowTwoWidgetSize)) frame_change(anm2::MULTIPLY);
ImGui::SetItemTooltip("Multiply the specified values for each frame.\n(Boolean values will simply be set.)");
if (ImGui::Button(localize.get(LABEL_MULTIPLY), rowTwoWidgetSize)) frame_change(anm2::MULTIPLY);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_MULTIPLY_VALUES));
ImGui::SameLine();
if (ImGui::Button("Divide", rowTwoWidgetSize)) frame_change(anm2::DIVIDE);
ImGui::SetItemTooltip("Divide the specified values for each frame.\n(Boolean values will simply be set.)");
if (ImGui::Button(localize.get(LABEL_DIVIDE), rowTwoWidgetSize)) frame_change(anm2::DIVIDE);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_DIVIDE_VALUES));
}
}
ImGui::End();
}
}
}

View File

@@ -2,7 +2,9 @@
#include <ranges>
#include "log.h"
#include "map_.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::util;
@@ -23,7 +25,7 @@ namespace anm2ed::imgui
hovered = -1;
if (ImGui::Begin("Layers", &settings.windowIsLayers))
if (ImGui::Begin(localize.get(LABEL_LAYERS_WINDOW), &settings.windowIsLayers))
{
auto childSize = size_without_footer_get();
@@ -38,7 +40,10 @@ namespace anm2ed::imgui
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
ImGui::Selectable(std::format(anm2::LAYER_FORMAT, id, layer.name, layer.spritesheetID).c_str(), isSelected);
ImGui::Selectable(
std::vformat(localize.get(FORMAT_LAYER), std::make_format_args(id, layer.name, layer.spritesheetID))
.c_str(),
isSelected);
if (newLayerId == id)
{
ImGui::SetScrollHereY(0.5f);
@@ -57,8 +62,9 @@ namespace anm2ed::imgui
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(layer.name.c_str());
ImGui::PopFont();
ImGui::Text("ID: %d", id);
ImGui::Text("Spritesheet ID: %d", layer.spritesheetID);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SPRITESHEET_ID), std::make_format_args(layer.spritesheetID)).c_str());
ImGui::EndTooltip();
}
ImGui::PopID();
@@ -82,11 +88,16 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
std::string errorString{};
document.snapshot("Paste Layer(s)");
document.snapshot(localize.get(EDIT_PASTE_LAYERS));
if (anm2.layers_deserialize(clipboard.get(), type, &errorString))
document.change(Document::NULLS);
else
toasts.error(std::format("Failed to deserialize layer(s): {}", errorString));
{
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_COPY], shortcut::FOCUSED)) copy();
@@ -94,13 +105,15 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
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))
copy();
if (ImGui::BeginMenu("Paste", !clipboard.is_empty()))
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
{
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
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();
}
@@ -113,13 +126,13 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize)) manager.layer_properties_open();
set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd);
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) manager.layer_properties_open();
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("Remove Unused", widgetSize))
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
{
auto remove_unused = [&]()
{
@@ -128,34 +141,33 @@ namespace anm2ed::imgui
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Layers", Document::LAYERS, remove_unused());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_LAYERS), Document::LAYERS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
settings.shortcutRemove);
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_LAYERS), settings.shortcutRemove);
}
ImGui::End();
manager.layer_properties_trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(propertiesPopup.label(), &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto childSize = child_size_get(2);
auto& layer = manager.editLayer;
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Child", childSize, ImGuiChildFlags_Borders))
{
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
input_text_string("Name", &layer.name);
ImGui::SetItemTooltip("Set the item's name.");
combo_negative_one_indexed("Spritesheet", &layer.spritesheetID, document.spritesheet.labels);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
input_text_string(localize.get(BASIC_NAME), &layer.name);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ITEM_NAME));
combo_negative_one_indexed(localize.get(LABEL_SPRITESHEET), &layer.spritesheetID, document.spritesheet.labels);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LAYER_SPRITESHEET));
}
ImGui::EndChild();
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
if (ImGui::Button(reference == -1 ? localize.get(BASIC_ADD) : localize.get(BASIC_CONFIRM), widgetSize))
{
if (reference == -1)
{
@@ -167,7 +179,7 @@ namespace anm2ed::imgui
newLayerId = id;
};
DOCUMENT_EDIT(document, "Add Layer", Document::LAYERS, add());
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_LAYER), Document::LAYERS, add());
}
else
{
@@ -177,7 +189,7 @@ namespace anm2ed::imgui
selection = {reference};
};
DOCUMENT_EDIT(document, "Set Layer Properties", Document::LAYERS, set());
DOCUMENT_EDIT(document, localize.get(EDIT_SET_LAYER_PROPERTIES), Document::LAYERS, set());
}
manager.layer_properties_close();
@@ -185,7 +197,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) manager.layer_properties_close();
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) manager.layer_properties_close();
manager.layer_properties_end();
ImGui::EndPopup();

View File

@@ -2,7 +2,9 @@
#include <ranges>
#include "log.h"
#include "map_.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::resource;
@@ -23,7 +25,7 @@ namespace anm2ed::imgui
hovered = -1;
if (ImGui::Begin("Nulls", &settings.windowIsNulls))
if (ImGui::Begin(localize.get(LABEL_NULLS_WINDOW), &settings.windowIsNulls))
{
auto childSize = size_without_footer_get();
@@ -39,7 +41,8 @@ namespace anm2ed::imgui
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
ImGui::Selectable(std::format(anm2::NULL_FORMAT, id, null.name).c_str(), isSelected);
ImGui::Selectable(std::vformat(localize.get(FORMAT_NULL), std::make_format_args(id, null.name)).c_str(),
isSelected);
if (newNullId == id)
{
ImGui::SetScrollHereY(0.5f);
@@ -58,7 +61,7 @@ namespace anm2ed::imgui
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(null.name.c_str());
ImGui::PopFont();
ImGui::Text("ID: %d", id);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
ImGui::EndTooltip();
}
ImGui::PopID();
@@ -82,11 +85,16 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
std::string errorString{};
document.snapshot("Paste Null(s)");
document.snapshot(localize.get(EDIT_PASTE_NULLS));
if (anm2.nulls_deserialize(clipboard.get(), type, &errorString))
document.change(Document::NULLS);
else
toasts.error(std::format("Failed to deserialize null(s): {}", errorString));
{
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_COPY], shortcut::FOCUSED)) copy();
@@ -94,13 +102,15 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, false);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, selection.empty() || hovered > -1)) copy();
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))
copy();
if (ImGui::BeginMenu("Paste", !clipboard.is_empty()))
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
{
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
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();
}
@@ -113,13 +123,13 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize)) manager.null_properties_open();
set_item_tooltip_shortcut("Add a null.", settings.shortcutAdd);
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) manager.null_properties_open();
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("Remove Unused", widgetSize))
if (ImGui::Button(localize.get(LABEL_REMOVE_UNUSED_NULLS), widgetSize))
{
auto remove_unused = [&]()
{
@@ -128,34 +138,34 @@ namespace anm2ed::imgui
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Events", Document::EVENTS, remove_unused());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_NULLS), Document::EVENTS, remove_unused());
}
ImGui::EndDisabled();
set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)", settings.shortcutRemove);
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_NULLS), settings.shortcutRemove);
}
ImGui::End();
manager.null_properties_trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(propertiesPopup.label(), &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto childSize = child_size_get(2);
auto& null = manager.editNull;
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Child", childSize, ImGuiChildFlags_Borders))
{
if (propertiesPopup.isJustOpened) ImGui::SetKeyboardFocusHere();
input_text_string("Name", &null.name);
ImGui::SetItemTooltip("Set the null's name.");
input_text_string(localize.get(BASIC_NAME), &null.name);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_NULL_NAME));
ImGui::Checkbox("Rect", &null.isShowRect);
ImGui::SetItemTooltip("The null will have a rectangle show around it.");
ImGui::Checkbox(localize.get(LABEL_RECT), &null.isShowRect);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_NULL_RECT));
}
ImGui::EndChild();
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button(reference == -1 ? "Add" : "Confirm", widgetSize))
if (ImGui::Button(reference == -1 ? localize.get(BASIC_ADD) : localize.get(BASIC_CONFIRM), widgetSize))
{
if (reference == -1)
{
@@ -167,7 +177,7 @@ namespace anm2ed::imgui
newNullId = id;
};
DOCUMENT_EDIT(document, "Add Null", Document::NULLS, add());
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_NULL), Document::NULLS, add());
}
else
{
@@ -177,7 +187,7 @@ namespace anm2ed::imgui
selection = {reference};
};
DOCUMENT_EDIT(document, "Set Null Properties", Document::NULLS, set());
DOCUMENT_EDIT(document, localize.get(EDIT_SET_NULL_PROPERTIES), Document::NULLS, set());
}
manager.null_properties_close();
@@ -185,7 +195,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) manager.null_properties_close();
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) manager.null_properties_close();
ImGui::EndPopup();
}

View File

@@ -3,6 +3,7 @@
#include <glm/gtc/type_ptr.hpp>
#include "imgui_.h"
#include "strings.h"
using namespace anm2ed::types;
using namespace glm;
@@ -20,30 +21,30 @@ namespace anm2ed::imgui
auto& afterColor = settings.onionskinAfterColor;
auto& mode = settings.onionskinMode;
if (ImGui::Begin("Onionskin", &settings.windowIsOnionskin))
if (ImGui::Begin(localize.get(LABEL_ONIONSKIN_WINDOW), &settings.windowIsOnionskin))
{
auto configure_widgets = [&](const char* separator, int& frames, vec3& color)
{
ImGui::PushID(separator);
ImGui::SeparatorText(separator);
input_int_range("Frames", frames, 0, FRAMES_MAX);
ImGui::SetItemTooltip("Change the amount of frames this onionskin will use.");
ImGui::ColorEdit3("Color", value_ptr(color));
ImGui::SetItemTooltip("Change the color of the frames this onionskin will use.");
input_int_range(localize.get(BASIC_FRAMES), frames, 0, FRAMES_MAX);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ONIONSKIN_FRAMES));
ImGui::ColorEdit3(localize.get(BASIC_COLOR), value_ptr(color));
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ONIONSKIN_COLOR));
ImGui::PopID();
};
ImGui::Checkbox("Enabled", &isEnabled);
set_item_tooltip_shortcut("Toggle onionskinning.", settings.shortcutOnionskin);
ImGui::Checkbox(localize.get(BASIC_ENABLED), &isEnabled);
set_item_tooltip_shortcut(localize.get(TOOLTIP_ONIONSKIN_ENABLED), settings.shortcutOnionskin);
configure_widgets("Before", beforeCount, beforeColor);
configure_widgets("After", afterCount, afterColor);
ImGui::SeparatorText("Mode");
ImGui::RadioButton("Time", &mode, (int)OnionskinMode::TIME);
ImGui::SetItemTooltip("The onionskinned frames will be based on frame time.");
configure_widgets(localize.get(BASIC_BEFORE), beforeCount, beforeColor);
configure_widgets(localize.get(BASIC_AFTER), afterCount, afterColor);
ImGui::SeparatorText(localize.get(BASIC_MODE));
ImGui::RadioButton(localize.get(BASIC_TIME), &mode, (int)OnionskinMode::TIME);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ONIONSKIN_TIME));
ImGui::SameLine();
ImGui::RadioButton("Index", &mode, (int)OnionskinMode::INDEX);
ImGui::SetItemTooltip("The onionskinned frames will be based on frame index.");
ImGui::RadioButton(localize.get(BASIC_INDEX), &mode, (int)OnionskinMode::INDEX);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ONIONSKIN_INDEX));
}
ImGui::End();

View File

@@ -2,6 +2,7 @@
#include <ranges>
#include "log.h"
#include "strings.h"
#include "toast.h"
@@ -22,7 +23,7 @@ namespace anm2ed::imgui
hovered = -1;
if (ImGui::Begin(localize.get(LABEL_SOUNDS), &settings.windowIsSounds))
if (ImGui::Begin(localize.get(LABEL_SOUNDS_WINDOW), &settings.windowIsSounds))
{
auto childSize = imgui::size_without_footer_get();
@@ -57,6 +58,11 @@ namespace anm2ed::imgui
ImGui::PopFont();
ImGui::Text("%s: %d", localize.get(BASIC_ID), id);
ImGui::Text("%s", localize.get(TOOLTIP_SOUNDS_PLAY));
if (!sound.is_valid())
{
ImGui::Spacing();
ImGui::TextWrapped("%s", localize.get(TOOLTIP_SOUND_INVALID));
}
ImGui::EndTooltip();
}
ImGui::PopID();
@@ -84,7 +90,11 @@ namespace anm2ed::imgui
if (anm2.sounds_deserialize(clipboard.get(), document.directory_get().string(), type, &errorString))
document.change(Document::SOUNDS);
else
toasts.error(std::format("{}: {}", localize.get(TOAST_SOUNDS_PASTE_ERROR), errorString));
{
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();
@@ -99,8 +109,8 @@ namespace anm2ed::imgui
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
{
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
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();
}
@@ -113,13 +123,13 @@ namespace anm2ed::imgui
auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize)) dialog.file_open(dialog::SOUND_OPEN);
imgui::set_item_tooltip_shortcut("Add a sound.", settings.shortcutAdd);
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize)) dialog.file_open(dialog::SOUND_OPEN);
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_SOUND_ADD), settings.shortcutAdd);
ImGui::SameLine();
imgui::shortcut(manager.chords[SHORTCUT_REMOVE]);
ImGui::BeginDisabled(unused.empty());
if (ImGui::Button("Remove Unused", widgetSize))
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), widgetSize))
{
auto remove_unused = [&]()
{
@@ -128,11 +138,10 @@ namespace anm2ed::imgui
unused.clear();
};
DOCUMENT_EDIT(document, "Remove Unused Sounds", Document::SOUNDS, remove_unused());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SOUNDS), Document::SOUNDS, remove_unused());
};
ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused sounds (i.e., ones not used in any trigger.)",
settings.shortcutRemove);
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_SOUNDS), settings.shortcutRemove);
}
ImGui::End();
@@ -145,13 +154,19 @@ namespace anm2ed::imgui
{
selection = {id};
newSoundId = id;
toasts.info(std::format("Initialized sound #{}: {}", id, dialog.path));
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.error(std::format("Failed to initialize sound: {}", dialog.path));
{
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, "Add Sound", Document::SOUNDS, add());
DOCUMENT_EDIT(document, localize.get(EDIT_ADD_SOUND), Document::SOUNDS, add());
dialog.reset();
}

View File

@@ -7,6 +7,7 @@
#include "imgui_.h"
#include "imgui_internal.h"
#include "math_.h"
#include "strings.h"
#include "tool.h"
#include "types.h"
@@ -79,7 +80,7 @@ namespace anm2ed::imgui
auto center_view = [&]() { pan = -size * 0.5f; };
if (ImGui::Begin("Spritesheet Editor", &settings.windowIsSpritesheetEditor))
if (ImGui::Begin(localize.get(LABEL_SPRITESHEET_EDITOR_WINDOW), &settings.windowIsSpritesheetEditor))
{
auto childSize = ImVec2(imgui::row_widget_width_get(3),
@@ -87,20 +88,20 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Grid Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::Checkbox("Grid", &isGrid);
ImGui::SetItemTooltip("Toggle the visibility of the grid.");
ImGui::Checkbox(localize.get(BASIC_GRID), &isGrid);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_VISIBILITY));
ImGui::SameLine();
ImGui::Checkbox("Snap", &isGridSnap);
ImGui::SetItemTooltip("Cropping will snap points to the grid.");
ImGui::Checkbox(localize.get(LABEL_SNAP), &isGridSnap);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_SNAP));
ImGui::SameLine();
ImGui::ColorEdit4("Color", value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the grid's color.");
ImGui::ColorEdit4(localize.get(BASIC_COLOR), value_ptr(gridColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_COLOR));
input_int2_range("Size", gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("Change the size of all cells in the grid.");
input_int2_range(localize.get(BASIC_SIZE), gridSize, ivec2(GRID_SIZE_MIN), ivec2(GRID_SIZE_MAX));
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_SIZE));
input_int2_range("Offset", gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("Change the offset of the grid.");
input_int2_range(localize.get(BASIC_OFFSET), gridOffset, ivec2(GRID_OFFSET_MIN), ivec2(GRID_OFFSET_MAX));
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GRID_OFFSET));
}
ImGui::EndChild();
@@ -108,23 +109,26 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##View Child", childSize, true, ImGuiWindowFlags_HorizontalScrollbar))
{
ImGui::InputFloat("Zoom", &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("Change the zoom of the editor.");
ImGui::InputFloat(localize.get(BASIC_ZOOM), &zoom, zoomStep, zoomStep, "%.0f%%");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_EDITOR_ZOOM));
auto widgetSize = ImVec2(imgui::row_widget_width_get(2), 0);
imgui::shortcut(manager.chords[SHORTCUT_CENTER_VIEW]);
if (ImGui::Button("Center View", widgetSize)) center_view();
imgui::set_item_tooltip_shortcut("Centers the view.", settings.shortcutCenterView);
if (ImGui::Button(localize.get(LABEL_CENTER_VIEW), widgetSize)) center_view();
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_CENTER_VIEW), settings.shortcutCenterView);
ImGui::SameLine();
imgui::shortcut(manager.chords[SHORTCUT_FIT]);
if (ImGui::Button("Fit", widgetSize))
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});
imgui::set_item_tooltip_shortcut("Set the view to match the extent of the spritesheet.", settings.shortcutFit);
imgui::set_item_tooltip_shortcut(localize.get(TOOLTIP_FIT), settings.shortcutFit);
ImGui::TextUnformatted(std::format(POSITION_FORMAT, (int)mousePos.x, (int)mousePos.y).c_str());
auto mousePosInt = ivec2(mousePos);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_POSITION_SPACED), std::make_format_args(mousePosInt.x, mousePosInt.y))
.c_str());
}
ImGui::EndChild();
@@ -136,11 +140,16 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Background Child 1", subChildSize))
{
ImGui::ColorEdit3("Background", value_ptr(backgroundColor), ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("Change the background color.");
ImGui::BeginDisabled(isTransparent);
{
ImGui::ColorEdit3(localize.get(LABEL_BACKGROUND_COLOR), value_ptr(backgroundColor),
ImGuiColorEditFlags_NoInputs);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BACKGROUND_COLOR));
}
ImGui::EndDisabled();
ImGui::Checkbox("Border", &isBorder);
ImGui::SetItemTooltip("Toggle a border appearing around the spritesheet.");
ImGui::Checkbox(localize.get(LABEL_BORDER), &isBorder);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SPRITESHEET_BORDER));
}
ImGui::EndChild();
@@ -148,8 +157,8 @@ namespace anm2ed::imgui
if (ImGui::BeginChild("##Background Child 2", subChildSize))
{
ImGui::Checkbox("Transparent", &isTransparent);
ImGui::SetItemTooltip("Toggle the spritesheet editor being transparent.");
ImGui::Checkbox(localize.get(LABEL_TRANSPARENT), &isTransparent);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_TRANSPARENT));
}
ImGui::EndChild();
@@ -201,10 +210,11 @@ namespace anm2ed::imgui
unbind();
sync_checker_pan();
render_checker_background(drawList, min, max, -size * 0.5f - checkerPan, CHECKER_SIZE);
if (!isTransparent) drawList->AddRectFilled(min, max, ImGui::GetColorU32(to_imvec4(vec4(backgroundColor, 1.0f))));
drawList->AddImage(texture, min, max);
ImGui::InvisibleButton("##Spritesheet Editor", to_imvec2(size));
if (isTransparent)
render_checker_background(drawList, min, max, -size * 0.5f - checkerPan, CHECKER_SIZE);
else
drawList->AddRectFilled(min, max, ImGui::GetColorU32(to_imvec4(vec4(backgroundColor, 1.0f))));
ImGui::Image(texture, to_imvec2(size));
if (ImGui::IsItemHovered())
{
@@ -299,7 +309,7 @@ namespace anm2ed::imgui
break;
case tool::MOVE:
if (!frame) break;
if (isBegin) document.snapshot("Frame Pivot");
if (isBegin) document.snapshot(localize.get(EDIT_FRAME_PIVOT));
if (isMouseDown) frame->pivot = vec2(ivec2(mousePos - frame->crop));
if (isLeftPressed) frame->pivot.x -= step;
if (isRightPressed) frame->pivot.x += step;
@@ -310,9 +320,9 @@ namespace anm2ed::imgui
{
if (ImGui::BeginTooltip())
{
auto pivotFormat = math::vec2_format_get(frame->pivot);
auto pivotString = std::format("Pivot: ({}, {})", pivotFormat, pivotFormat);
ImGui::Text(pivotString.c_str(), frame->pivot.x, frame->pivot.y);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_PIVOT), std::make_format_args(frame->pivot.x, frame->pivot.y))
.c_str());
ImGui::EndTooltip();
}
}
@@ -321,7 +331,7 @@ namespace anm2ed::imgui
break;
case tool::CROP:
if (!frame) break;
if (isBegin) document.snapshot("Frame Crop");
if (isBegin) document.snapshot(localize.get(EDIT_FRAME_CROP));
if (isMouseClicked)
{
@@ -358,12 +368,12 @@ namespace anm2ed::imgui
}
if (ImGui::BeginTooltip())
{
auto cropFormat = math::vec2_format_get(frame->crop);
auto sizeFormat = math::vec2_format_get(frame->size);
auto cropString = std::format("Crop: ({}, {})", cropFormat, cropFormat);
auto sizeString = std::format("Size: ({}, {})", sizeFormat, sizeFormat);
ImGui::Text(cropString.c_str(), frame->crop.x, frame->crop.y);
ImGui::Text(sizeString.c_str(), frame->size.x, frame->size.y);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_CROP), std::make_format_args(frame->crop.x, frame->crop.y))
.c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SIZE), std::make_format_args(frame->size.x, frame->size.y))
.c_str());
ImGui::EndTooltip();
}
}
@@ -374,7 +384,8 @@ namespace anm2ed::imgui
{
if (!spritesheet) break;
auto color = useTool == tool::DRAW ? toolColor : vec4();
if (isMouseClicked) document.snapshot(useTool == tool::DRAW ? "Draw" : "Erase");
if (isMouseClicked)
document.snapshot(useTool == tool::DRAW ? localize.get(EDIT_DRAW) : localize.get(EDIT_ERASE));
if (isMouseDown) spritesheet->texture.pixel_line(ivec2(previousMousePos), ivec2(mousePos), color);
if (isMouseReleased) document.change(Document::SPRITESHEETS);
break;

View File

@@ -2,11 +2,17 @@
#include <ranges>
#include <format>
#include "log.h"
#include "document.h"
#include "filesystem_.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::types;
using namespace anm2ed::resource;
using namespace anm2ed::util;
using namespace glm;
namespace anm2ed::imgui
@@ -23,7 +29,7 @@ namespace anm2ed::imgui
hovered = -1;
if (ImGui::Begin("Spritesheets", &settings.windowIsSpritesheets))
if (ImGui::Begin(localize.get(LABEL_SPRITESHEETS_WINDOW), &settings.windowIsSpritesheets))
{
auto style = ImGui::GetStyle();
@@ -45,11 +51,16 @@ namespace anm2ed::imgui
auto paste = [&](merge::Type type)
{
std::string errorString{};
document.snapshot("Paste Spritesheet(s)");
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get().string(), type, &errorString))
document.change(Document::SPRITESHEETS);
else
toasts.error(std::format("Failed to deserialize spritesheet(s): {}", errorString));
document.snapshot(localize.get(EDIT_PASTE_SPRITESHEETS));
if (anm2.spritesheets_deserialize(clipboard.get(), document.directory_get().string(), type, &errorString))
document.change(Document::SPRITESHEETS);
else
{
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();
@@ -59,14 +70,16 @@ namespace anm2ed::imgui
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, true);
ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, true);
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !selection.empty() || hovered > -1)) copy();
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false,
!selection.empty() || hovered > -1))
copy();
if (ImGui::BeginMenu("Paste", !clipboard.is_empty()))
if (ImGui::BeginMenu(localize.get(BASIC_PASTE), !clipboard.is_empty()))
{
if (ImGui::MenuItem("Append", settings.shortcutPaste.c_str())) paste(merge::APPEND);
if (ImGui::MenuItem("Replace")) paste(merge::REPLACE);
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();
}
@@ -99,14 +112,21 @@ namespace anm2ed::imgui
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);
const std::string pathString =
spritesheet.path.empty() ? std::string{anm2::NO_PATH} : spritesheet.path.string();
const char* pathCStr = pathString.c_str();
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()) hovered = id;
if (ImGui::IsItemHovered())
{
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
filesystem::WorkingDirectory workingDirectory(document.directory_get());
dialog.file_explorer_open(spritesheet.path.parent_path().string());
}
hovered = id;
}
if (newSpritesheetId == id)
{
ImGui::SetScrollHereY(0.5f);
@@ -150,12 +170,16 @@ namespace anm2ed::imgui
ImGui::TextUnformatted(pathCStr);
ImGui::PopFont();
ImGui::Text("ID: %d", id);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
if (!isTextureValid)
ImGui::Text("This spritesheet isn't valid!\nLoad an existing, valid texture.");
ImGui::TextUnformatted(localize.get(TOOLTIP_SPRITESHEET_INVALID));
else
ImGui::Text("Size: %d x %d", texture.size.x, texture.size.y);
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_TEXTURE_SIZE),
std::make_format_args(texture.size.x, texture.size.y))
.c_str());
ImGui::TextUnformatted(localize.get(TEXT_OPEN_DIRECTORY));
}
ImGui::EndChild();
@@ -179,7 +203,8 @@ namespace anm2ed::imgui
spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
ImGui::Text(anm2::SPRITESHEET_FORMAT_C, id, pathCStr);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SPRITESHEET), std::make_format_args(id, pathCStr)).c_str());
if (isReferenced) ImGui::PopFont();
context_menu();
@@ -200,8 +225,8 @@ namespace anm2ed::imgui
auto rowOneWidgetSize = widget_size_with_row_get(3);
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_OPEN);
set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd);
if (ImGui::Button(localize.get(BASIC_ADD), rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_OPEN);
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_SPRITESHEET), settings.shortcutAdd);
if (dialog.is_selected(dialog::SPRITESHEET_OPEN))
{
@@ -214,7 +239,7 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(selection.empty());
{
if (ImGui::Button("Reload", rowOneWidgetSize))
if (ImGui::Button(localize.get(BASIC_RELOAD), rowOneWidgetSize))
{
auto reload = [&]()
{
@@ -222,13 +247,17 @@ namespace anm2ed::imgui
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet.reload(document.directory_get());
toasts.info(std::format("Reloaded spritesheet #{}: {}", id, spritesheet.path.string()));
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, "Reload Spritesheet(s)", Document::SPRITESHEETS, reload());
DOCUMENT_EDIT(document, localize.get(EDIT_RELOAD_SPRITESHEETS), Document::SPRITESHEETS, reload());
}
ImGui::SetItemTooltip("Reloads the selected spritesheets.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_RELOAD_SPRITESHEETS));
}
ImGui::EndDisabled();
@@ -236,8 +265,8 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(selection.size() != 1);
{
if (ImGui::Button("Replace", rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_REPLACE);
ImGui::SetItemTooltip("Replace the selected spritesheet with a new one.");
if (ImGui::Button(localize.get(BASIC_REPLACE), rowOneWidgetSize)) dialog.file_open(dialog::SPRITESHEET_REPLACE);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_REPLACE_SPRITESHEET));
}
ImGui::EndDisabled();
@@ -248,10 +277,13 @@ namespace anm2ed::imgui
auto& id = *selection.begin();
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
spritesheet = anm2::Spritesheet(document.directory_get().string(), dialog.path);
toasts.info(std::format("Replaced spritesheet #{}: {}", id, spritesheet.path.string()));
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, "Replace Spritesheet", Document::SPRITESHEETS, replace());
DOCUMENT_EDIT(document, localize.get(EDIT_REPLACE_SPRITESHEET), Document::SPRITESHEETS, replace());
dialog.reset();
}
@@ -260,23 +292,27 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(unused.empty());
{
shortcut(manager.chords[SHORTCUT_REMOVE]);
if (ImGui::Button("Remove Unused", rowTwoWidgetSize))
if (ImGui::Button(localize.get(BASIC_REMOVE_UNUSED), rowTwoWidgetSize))
{
auto remove_unused = [&]()
{
for (auto& id : unused)
{
anm2::Spritesheet& spritesheet = anm2.content.spritesheets[id];
toasts.info(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
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, "Remove Unused Spritesheets", Document::SPRITESHEETS, remove_unused());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_UNUSED_SPRITESHEETS), Document::SPRITESHEETS,
remove_unused());
}
set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
settings.shortcutRemove);
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_UNUSED_SPRITESHEETS), settings.shortcutRemove);
}
ImGui::EndDisabled();
@@ -284,20 +320,31 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(selection.empty());
{
if (ImGui::Button("Save", rowTwoWidgetSize))
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.info(std::format("Saved spritesheet #{}: {}", id, spritesheet.path.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.info(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string()));
{
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)));
}
}
}
}
ImGui::EndDisabled();
ImGui::SetItemTooltip("Save the selected spritesheets.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SAVE_SPRITESHEETS));
}
ImGui::End();
}

View File

@@ -7,6 +7,7 @@
#include <imgui_internal.h>
#include "log.h"
#include "math_.h"
#include "toast.h"
@@ -82,16 +83,6 @@ namespace anm2ed::imgui
constexpr auto FRAME_TOOLTIP_HOVER_DELAY = 0.75f; // Extra delay for frame info tooltip.
constexpr int ITEM_SELECTION_NULL_FLAG = 1 << 30;
constexpr auto HELP_FORMAT = R"(- Press {} to decrement time.
- Press {} to increment time.
- Press {} to shorten the selected frame, by one frame.
- Press {} to extend the selected frame, by one frame.
- Press {} to go to the previous frame.
- Press {} to go to the next frame.
- Click and hold on a frame while holding CTRL to change its duration.
- Click and hold on a trigger to change its At Frame.
- Hold Alt while clicking a non-trigger frame to toggle interpolation.)";
void Timeline::update(Manager& manager, Settings& settings, Resources& resources, Clipboard& clipboard)
{
auto& document = *manager.get();
@@ -325,7 +316,10 @@ namespace anm2ed::imgui
item->frames.erase(item->frames.begin() + i);
}
reference.frameIndex = glm::clamp(--reference.frameIndex, -1, (int)item->frames.size() - 1);
if (item->frames.empty())
reference.frameIndex = -1;
else
reference.frameIndex = glm::clamp(--reference.frameIndex, 0, (int)item->frames.size() - 1);
frames_selection_set_reference();
}
};
@@ -376,14 +370,14 @@ namespace anm2ed::imgui
auto cut = [&]()
{
copy();
DOCUMENT_EDIT(document, "Cut Frame(s)", Document::FRAMES, frames_delete());
DOCUMENT_EDIT(document, localize.get(EDIT_CUT_FRAMES), Document::FRAMES, frames_delete());
};
auto paste = [&]()
{
if (auto item = animation->item_get(reference.itemType, reference.itemID))
{
document.snapshot("Paste Frame(s)");
document.snapshot(localize.get(EDIT_PASTE_FRAMES));
std::set<int> indices{};
std::string errorString{};
int insertIndex = (int)item->frames.size();
@@ -403,10 +397,17 @@ namespace anm2ed::imgui
document.change(Document::FRAMES);
}
else
toasts.error(std::format("Failed to deserialize frame(s): {}", errorString));
{
toasts.push(std::format("{} {}", localize.get(TOAST_DESERIALIZE_FRAMES_FAILED), errorString));
logger.error(std::format("{} {}", localize.get(TOAST_DESERIALIZE_FRAMES_FAILED, anm2ed::ENGLISH),
errorString));
}
}
else
toasts.error(std::format("Failed to deserialize frame(s): select an item first!"));
{
toasts.push(localize.get(TOAST_DESERIALIZE_FRAMES_NO_SELECTION));
logger.warning(localize.get(TOAST_DESERIALIZE_FRAMES_NO_SELECTION, anm2ed::ENGLISH));
}
};
if (shortcut(manager.chords[SHORTCUT_CUT], shortcut::FOCUSED)) cut();
@@ -415,9 +416,11 @@ namespace anm2ed::imgui
if (ImGui::BeginPopupContextWindow("##Context Menu", ImGuiPopupFlags_MouseButtonRight))
{
if (ImGui::MenuItem("Cut", settings.shortcutCut.c_str(), false, !frames.selection.empty())) cut();
if (ImGui::MenuItem("Copy", settings.shortcutCopy.c_str(), false, !frames.selection.empty())) copy();
if (ImGui::MenuItem("Paste", nullptr, false, !clipboard.is_empty())) paste();
if (ImGui::MenuItem(localize.get(BASIC_CUT), settings.shortcutCut.c_str(), false, !frames.selection.empty()))
cut();
if (ImGui::MenuItem(localize.get(BASIC_COPY), settings.shortcutCopy.c_str(), false, !frames.selection.empty()))
copy();
if (ImGui::MenuItem(localize.get(BASIC_PASTE), nullptr, false, !clipboard.is_empty())) paste();
ImGui::EndPopup();
}
@@ -529,10 +532,12 @@ namespace anm2ed::imgui
if (isOnlyShowLayers && type != anm2::LAYER) isVisible = false;
auto isActive = reference.itemType == type && reference.itemID == id;
auto label = type == anm2::LAYER ? std::format(anm2::LAYER_FORMAT, id, anm2.content.layers[id].name,
anm2.content.layers[id].spritesheetID)
: type == anm2::NULL_ ? std::format(anm2::NULL_FORMAT, id, anm2.content.nulls[id].name)
: anm2::TYPE_STRINGS[type];
auto label = type == anm2::LAYER ? std::vformat(localize.get(FORMAT_LAYER),
std::make_format_args(id, anm2.content.layers[id].name,
anm2.content.layers[id].spritesheetID))
: type == anm2::NULL_
? std::vformat(localize.get(FORMAT_NULL), std::make_format_args(id, anm2.content.nulls[id].name))
: localize.get(anm2::TYPE_STRINGS[type]);
auto icon = anm2::TYPE_ICONS[type];
auto iconTintCurrent = isLightTheme && type == anm2::NONE ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : itemIconTint;
auto baseColorVec = item_color_vec(type);
@@ -605,8 +610,9 @@ namespace anm2ed::imgui
reference_set_item(type, id);
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) items.hovered = id;
if (ImGui::IsItemHovered())
bool isItemHovered = ImGui::IsItemHovered();
if (isItemHovered) items.hovered = id;
if (isItemHovered)
{
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
@@ -623,6 +629,98 @@ namespace anm2ed::imgui
}
}
if (isItemHovered)
{
auto& imguiStyle = ImGui::GetStyle();
auto previousTooltipFlags = imguiStyle.HoverFlagsForTooltipMouse;
auto previousTooltipDelay = imguiStyle.HoverDelayNormal;
imguiStyle.HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal |
ImGuiHoveredFlags_AllowWhenDisabled |
ImGuiHoveredFlags_NoSharedDelay;
imguiStyle.HoverDelayNormal = FRAME_TOOLTIP_HOVER_DELAY;
bool showItemTooltip = ImGui::BeginItemTooltip();
imguiStyle.HoverFlagsForTooltipMouse = previousTooltipFlags;
imguiStyle.HoverDelayNormal = previousTooltipDelay;
if (showItemTooltip)
{
auto yesNoLabel = [&](bool value) { return value ? localize.get(BASIC_YES) : localize.get(BASIC_NO); };
auto visibleLabel = yesNoLabel(isVisible);
auto framesCount = item ? (int)item->frames.size() : 0;
switch (type)
{
case anm2::ROOT:
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(localize.get(BASIC_ROOT));
ImGui::PopFont();
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_VISIBLE), std::make_format_args(visibleLabel)).c_str());
auto transformLabel = yesNoLabel(settings.previewIsRootTransform);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_TRANSFORM), std::make_format_args(transformLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_FRAMES_COUNT), std::make_format_args(framesCount)).c_str());
break;
}
case anm2::LAYER:
{
auto& layer = anm2.content.layers[id];
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(layer.name.c_str());
ImGui::PopFont();
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
ImGui::TextUnformatted(std::vformat(localize.get(FORMAT_SPRITESHEET_ID),
std::make_format_args(layer.spritesheetID))
.c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_VISIBLE), std::make_format_args(visibleLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_FRAMES_COUNT), std::make_format_args(framesCount)).c_str());
break;
}
case anm2::NULL_:
{
auto& nullInfo = anm2.content.nulls[id];
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(nullInfo.name.c_str());
ImGui::PopFont();
auto rectLabel = yesNoLabel(nullInfo.isShowRect);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_ID), std::make_format_args(id)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_RECT), std::make_format_args(rectLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_VISIBLE), std::make_format_args(visibleLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_FRAMES_COUNT), std::make_format_args(framesCount)).c_str());
break;
}
case anm2::TRIGGER:
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(localize.get(BASIC_TRIGGERS));
ImGui::PopFont();
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_VISIBLE), std::make_format_args(visibleLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_TRIGGERS_COUNT), std::make_format_args(framesCount)).c_str());
break;
}
default:
break;
}
ImGui::EndTooltip();
}
}
if (type == anm2::LAYER)
{
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip))
@@ -645,7 +743,7 @@ namespace anm2ed::imgui
if (source != -1 && destination != -1) vector::move_index(animation->layerOrder, source, destination);
};
DOCUMENT_EDIT(document, "Move Layer Animation", Document::ITEMS, layer_order_move());
DOCUMENT_EDIT(document, localize.get(EDIT_MOVE_LAYER_ANIMATION), Document::ITEMS, layer_order_move());
}
ImGui::EndDragDropTarget();
}
@@ -673,9 +771,11 @@ namespace anm2ed::imgui
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
int visibleIcon = item->isVisible ? icon::VISIBLE : icon::INVISIBLE;
if (ImGui::ImageButton("##Visible Toggle", resources.icons[visibleIcon].id, icon_size_get()))
DOCUMENT_EDIT(document, "Item Visibility", Document::FRAMES, item->isVisible = !item->isVisible);
DOCUMENT_EDIT(document, localize.get(EDIT_TOGGLE_ITEM_VISIBILITY), Document::FRAMES,
item->isVisible = !item->isVisible);
overlay_icon(resources.icons[visibleIcon].id, iconTintCurrent);
ImGui::SetItemTooltip(isVisible ? "The item is shown. Press to hide." : "The item is hidden. Press to show.");
ImGui::SetItemTooltip("%s", isVisible ? localize.get(TOOLTIP_ITEM_VISIBILITY_SHOWN)
: localize.get(TOOLTIP_ITEM_VISIBILITY_HIDDEN));
if (type == anm2::NULL_)
{
@@ -686,10 +786,11 @@ namespace anm2ed::imgui
ImVec2(itemSize.x - (ImGui::GetTextLineHeightWithSpacing() * 2) - ImGui::GetStyle().ItemSpacing.x,
(itemSize.y - ImGui::GetTextLineHeightWithSpacing()) / 2));
if (ImGui::ImageButton("##Rect Toggle", resources.icons[rectIcon].id, icon_size_get()))
DOCUMENT_EDIT(document, "Null Rect", Document::FRAMES, null.isShowRect = !null.isShowRect);
DOCUMENT_EDIT(document, localize.get(EDIT_TOGGLE_NULL_RECT), Document::FRAMES,
null.isShowRect = !null.isShowRect);
overlay_icon(resources.icons[rectIcon].id, iconTintCurrent);
ImGui::SetItemTooltip(isShowRect ? "The null's rect is shown. Press to hide."
: "The null's rect is hidden. Press to show.");
ImGui::SetItemTooltip("%s", isShowRect ? localize.get(TOOLTIP_NULL_RECT_SHOWN)
: localize.get(TOOLTIP_NULL_RECT_HIDDEN));
}
ImGui::PopStyleVar(2);
@@ -712,8 +813,8 @@ namespace anm2ed::imgui
if (ImGui::ImageButton("##Unused Toggle", resources.icons[unusedIcon].id, icon_size_get()))
isShowUnused = !isShowUnused;
overlay_icon(resources.icons[unusedIcon].id, iconTintCurrent);
ImGui::SetItemTooltip(isShowUnused ? "Unused layers/nulls are shown. Press to hide."
: "Unused layers/nulls are hidden. Press to show.");
ImGui::SetItemTooltip("%s", isShowUnused ? localize.get(TOOLTIP_UNUSED_ITEMS_SHOWN)
: localize.get(TOOLTIP_UNUSED_ITEMS_HIDDEN));
auto& showLayersOnly = settings.timelineIsOnlyShowLayers;
auto layersIcon = showLayersOnly ? icon::SHOW_LAYERS : icon::HIDE_LAYERS;
@@ -723,8 +824,8 @@ namespace anm2ed::imgui
if (ImGui::ImageButton("##Layers Toggle", resources.icons[layersIcon].id, icon_size_get()))
showLayersOnly = !showLayersOnly;
overlay_icon(resources.icons[layersIcon].id, iconTintCurrent);
ImGui::SetItemTooltip(showLayersOnly ? "Only layers are visible. Press to show all items."
: "All items are visible. Press to only show layers.");
ImGui::SetItemTooltip("%s", showLayersOnly ? localize.get(TOOLTIP_ONLY_LAYERS_VISIBLE)
: localize.get(TOOLTIP_ALL_ITEMS_VISIBLE));
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
@@ -734,11 +835,12 @@ namespace anm2ed::imgui
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::Text("(?)");
ImGui::PopStyleColor();
ImGui::SetItemTooltip("%s", std::format(HELP_FORMAT, settings.shortcutMovePlayheadBack,
settings.shortcutMovePlayheadForward, settings.shortcutShortenFrame,
settings.shortcutExtendFrame, settings.shortcutPreviousFrame,
settings.shortcutNextFrame)
.c_str());
auto tooltipShortcuts = std::vformat(
localize.get(TOOLTIP_TIMELINE_SHORTCUTS),
std::make_format_args(settings.shortcutMovePlayheadBack, settings.shortcutMovePlayheadForward,
settings.shortcutShortenFrame, settings.shortcutExtendFrame,
settings.shortcutPreviousFrame, settings.shortcutNextFrame));
ImGui::SetItemTooltip("%s", tooltipShortcuts.c_str());
ImGui::EndDisabled();
}
}
@@ -863,12 +965,12 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(!animation);
{
shortcut(manager.chords[SHORTCUT_ADD]);
if (ImGui::Button("Add", widgetSize))
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
{
item_properties_reset();
propertiesPopup.open();
}
set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
set_item_tooltip_shortcut(localize.get(TOOLTIP_ADD_ITEM), settings.shortcutAdd);
ImGui::SameLine();
auto selectionType = item_selection_type_get();
@@ -877,7 +979,7 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(!hasSelection && !hasReferenceItem);
{
shortcut(manager.chords[SHORTCUT_REMOVE]);
if (ImGui::Button("Remove", widgetSize))
if (ImGui::Button(localize.get(BASIC_REMOVE), widgetSize))
{
auto remove = [&]()
{
@@ -897,9 +999,9 @@ namespace anm2ed::imgui
reference_clear();
};
DOCUMENT_EDIT(document, "Remove Item(s)", Document::ITEMS, remove());
DOCUMENT_EDIT(document, localize.get(EDIT_REMOVE_ITEMS), Document::ITEMS, remove());
}
set_item_tooltip_shortcut("Remove the selected item(s) from the animation.", settings.shortcutRemove);
set_item_tooltip_shortcut(localize.get(TOOLTIP_REMOVE_ITEMS), settings.shortcutRemove);
}
ImGui::EndDisabled();
}
@@ -956,7 +1058,7 @@ namespace anm2ed::imgui
auto drawList = ImGui::GetWindowDrawList();
auto clipMax = drawList->GetClipRectMax();
auto length = animation ? animation->frameNum : anm2.animations.length();
auto length = animation ? animation->frameNum : 0;
auto frameSize = ImVec2(ImGui::GetTextLineHeight(), ImGui::GetContentRegionAvail().y);
auto framesSize = ImVec2(frameSize.x * length, frameSize.y);
auto cursorPos = ImGui::GetCursorPos();
@@ -969,20 +1071,23 @@ namespace anm2ed::imgui
if (type == anm2::NONE)
{
if (isLightTheme)
if (length > 0)
{
auto totalMax = ImVec2(cursorScreenPos.x + framesSize.x, cursorScreenPos.y + framesSize.y);
drawList->AddRectFilled(cursorScreenPos, totalMax, ImGui::GetColorU32(timelineBackgroundColor));
float animationWidth = std::min(framesSize.x, frameSize.x * (float)length);
drawList->AddRectFilled(cursorScreenPos,
ImVec2(cursorScreenPos.x + animationWidth, cursorScreenPos.y + framesSize.y),
ImGui::GetColorU32(timelinePlayheadRectColor));
}
else
{
drawList->AddRectFilled(cursorScreenPos,
ImVec2(cursorScreenPos.x + framesSize.x, cursorScreenPos.y + framesSize.y),
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Header)));
if (isLightTheme)
{
auto totalMax = ImVec2(cursorScreenPos.x + framesSize.x, cursorScreenPos.y + framesSize.y);
drawList->AddRectFilled(cursorScreenPos, totalMax, ImGui::GetColorU32(timelineBackgroundColor));
float animationWidth = std::min(framesSize.x, frameSize.x * (float)length);
drawList->AddRectFilled(cursorScreenPos,
ImVec2(cursorScreenPos.x + animationWidth, cursorScreenPos.y + framesSize.y),
ImGui::GetColorU32(timelinePlayheadRectColor));
}
else
{
drawList->AddRectFilled(cursorScreenPos,
ImVec2(cursorScreenPos.x + framesSize.x, cursorScreenPos.y + framesSize.y),
ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Header)));
}
}
for (int i = frameMin; i < frameMax; i++)
@@ -1029,9 +1134,12 @@ namespace anm2ed::imgui
if (ImGui::IsMouseReleased(0)) isDragging = false;
ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * floorf(playback.time), cursorPos.y));
ImGui::Image(resources.icons[icon::PLAYHEAD].id, frameSize);
overlay_icon(resources.icons[icon::PLAYHEAD].id, playheadIconTint, true);
if (length > 0)
{
ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * floorf(playback.time), cursorPos.y));
ImGui::Image(resources.icons[icon::PLAYHEAD].id, frameSize);
overlay_icon(resources.icons[icon::PLAYHEAD].id, playheadIconTint, true);
}
}
else if (animation)
{
@@ -1116,7 +1224,7 @@ namespace anm2ed::imgui
if (type != anm2::TRIGGER)
{
if (ImGui::IsKeyDown(ImGuiMod_Alt))
DOCUMENT_EDIT(document, "Frame Interpolation", Document::FRAMES,
DOCUMENT_EDIT(document, localize.get(EDIT_FRAME_INTERPOLATION), Document::FRAMES,
frame.isInterpolated = !frame.isInterpolated);
document.frameTime = frameTime;
@@ -1145,59 +1253,80 @@ namespace anm2ed::imgui
if (showFrameTooltip)
{
if (type != anm2::TRIGGER)
{
auto cropX = frame.crop.x;
auto cropY = frame.crop.y;
auto sizeX = frame.size.x;
auto sizeY = frame.size.y;
auto pivotX = frame.pivot.x;
auto pivotY = frame.pivot.y;
auto scaleX = frame.scale.x;
auto scaleY = frame.scale.y;
auto rotationValue = frame.rotation;
auto durationValue = frame.duration;
auto tintR = math::float_to_uint8(frame.tint.r);
auto tintG = math::float_to_uint8(frame.tint.g);
auto tintB = math::float_to_uint8(frame.tint.b);
auto tintA = math::float_to_uint8(frame.tint.a);
auto visibleLabel = frame.isVisible ? localize.get(BASIC_YES) : localize.get(BASIC_NO);
auto interpolatedLabel = frame.isInterpolated ? localize.get(BASIC_YES) : localize.get(BASIC_NO);
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::Text("%s Frame", anm2::TYPE_STRINGS[type]);
ImGui::Text("%s %s", localize.get(anm2::TYPE_STRINGS[type]), localize.get(BASIC_FRAME));
ImGui::PopFont();
auto float_text = [&](std::string label, float& value)
{
std::string useFormat = math::float_format_get(value);
std::string format(label + ": " + useFormat);
ImGui::Text(format.c_str(), value);
};
auto vec2_text = [&](std::string label, vec2& value)
{
std::string useFormat = math::vec2_format_get(value);
std::string format(label + ": " + useFormat + ", " + useFormat);
ImGui::Text(format.c_str(), value.x, value.y);
};
ImGui::Text("Index: %i", (int)i);
auto indexValue = (int)i;
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_INDEX), std::make_format_args(indexValue)).c_str());
if (type == anm2::LAYER)
{
vec2_text("Crop", frame.crop);
vec2_text("Size", frame.size);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_CROP), std::make_format_args(cropX, cropY)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SIZE), std::make_format_args(sizeX, sizeY)).c_str());
}
vec2_text("Position", frame.size);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_POSITION), std::make_format_args(sizeX, sizeY)).c_str());
if (type == anm2::LAYER) vec2_text("Pivot", frame.pivot);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_PIVOT), std::make_format_args(pivotX, pivotY)).c_str());
vec2_text("Scale", frame.scale);
float_text("Rotation", frame.rotation);
ImGui::Text("Duration: %i", frame.duration);
ImGui::Text("Tint: %i, %i, %i, %i", math::float_to_uint8(frame.tint.r),
math::float_to_uint8(frame.tint.g), math::float_to_uint8(frame.tint.b),
math::float_to_uint8(frame.tint.a));
ImGui::Text("Color Offset: %i, %i, %i", math::float_to_uint8(frame.tint.r),
math::float_to_uint8(frame.tint.g), math::float_to_uint8(frame.tint.b));
ImGui::Text("Visible: %s", frame.isVisible ? "true" : "false");
ImGui::Text("Interpolated: %s", frame.isInterpolated ? "true" : "false");
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SCALE), std::make_format_args(scaleX, scaleY)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_ROTATION), std::make_format_args(rotationValue)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_DURATION), std::make_format_args(durationValue)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_TINT), std::make_format_args(tintR, tintG, tintB, tintA)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_COLOR_OFFSET), std::make_format_args(tintR, tintG, tintB))
.c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_VISIBLE), std::make_format_args(visibleLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_INTERPOLATED), std::make_format_args(interpolatedLabel)).c_str());
}
else
{
auto atFrameValue = frame.atFrame;
auto eventLabel = document.event.labels[frame.eventID + 1];
auto soundLabel = document.sound.labels[frame.soundID + 1];
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::Text("%s", anm2::TYPE_STRINGS[type]);
ImGui::Text("%s %s", localize.get(anm2::TYPE_STRINGS[type]), localize.get(BASIC_FRAME));
ImGui::PopFont();
ImGui::Text("At Frame: %i", frame.atFrame);
ImGui::Text("Event: %s", document.event.labels[frame.eventID + 1]);
ImGui::Text("Sound: %s", document.sound.labels[frame.soundID + 1]);
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_AT_FRAME), std::make_format_args(atFrameValue)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_EVENT_LABEL), std::make_format_args(eventLabel)).c_str());
ImGui::TextUnformatted(
std::vformat(localize.get(FORMAT_SOUND_LABEL), std::make_format_args(soundLabel)).c_str());
}
ImGui::EndTooltip();
@@ -1294,7 +1423,7 @@ namespace anm2ed::imgui
int insertPosResult = -1;
int insertedCount = 0;
DOCUMENT_EDIT(document, "Move Frame(s)", Document::FRAMES, {
DOCUMENT_EDIT(document, localize.get(EDIT_MOVE_FRAMES), Document::FRAMES, {
std::vector<anm2::Frame> movedFrames;
movedFrames.reserve(indices.size());
@@ -1393,7 +1522,8 @@ namespace anm2ed::imgui
if (!isDraggedFrameSnapshot && hoveredTime != draggedFrameStart)
{
isDraggedFrameSnapshot = true;
document.snapshot(type == anm2::TRIGGER ? "Trigger At Frame" : "Frame Duration");
document.snapshot(type == anm2::TRIGGER ? localize.get(EDIT_TRIGGER_AT_FRAME)
: localize.get(EDIT_FRAME_DURATION));
}
if (type == anm2::TRIGGER)
@@ -1562,8 +1692,9 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(!animation);
{
auto label = playback.isPlaying ? "Pause" : "Play";
auto tooltip = playback.isPlaying ? "Pause the animation." : "Play the animation.";
auto label = playback.isPlaying ? localize.get(LABEL_PAUSE) : localize.get(LABEL_PLAY);
auto tooltip =
playback.isPlaying ? localize.get(TOOLTIP_PAUSE_ANIMATION) : localize.get(TOOLTIP_PLAY_ANIMATION);
shortcut(manager.chords[SHORTCUT_PLAY_PAUSE]);
if (ImGui::Button(label, widgetSize)) playback.toggle();
@@ -1576,7 +1707,7 @@ namespace anm2ed::imgui
ImGui::BeginDisabled(!item);
{
shortcut(manager.chords[SHORTCUT_INSERT_FRAME]);
if (ImGui::Button("Insert", widgetSize))
if (ImGui::Button(localize.get(LABEL_INSERT), widgetSize))
{
auto insert_frame = [&]()
{
@@ -1616,23 +1747,23 @@ namespace anm2ed::imgui
frames_selection_set_reference();
};
DOCUMENT_EDIT(document, "Insert Frame", Document::FRAMES, insert_frame());
DOCUMENT_EDIT(document, localize.get(EDIT_INSERT_FRAME), Document::FRAMES, insert_frame());
}
set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutInsertFrame);
set_item_tooltip_shortcut(localize.get(TOOLTIP_INSERT_FRAME), settings.shortcutInsertFrame);
ImGui::SameLine();
ImGui::BeginDisabled(!document.frame_get());
{
shortcut(manager.chords[SHORTCUT_REMOVE]);
if (ImGui::Button("Delete", widgetSize))
DOCUMENT_EDIT(document, "Delete Frame(s)", Document::FRAMES, frames_delete());
set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
if (ImGui::Button(localize.get(LABEL_DELETE), widgetSize))
DOCUMENT_EDIT(document, localize.get(EDIT_DELETE_FRAMES), Document::FRAMES, frames_delete());
set_item_tooltip_shortcut(localize.get(TOOLTIP_DELETE_FRAMES), settings.shortcutRemove);
ImGui::SameLine();
if (ImGui::Button("Bake", widgetSize)) bakePopup.open();
ImGui::SetItemTooltip("Turn interpolated frames into uninterpolated ones.");
if (ImGui::Button(localize.get(LABEL_BAKE), widgetSize)) bakePopup.open();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_FRAMES));
}
ImGui::EndDisabled();
}
@@ -1641,27 +1772,29 @@ namespace anm2ed::imgui
ImGui::SameLine();
ImGui::BeginDisabled(!animation || animation->frameNum == animation->length());
if (ImGui::Button("Fit Animation Length", widgetSize))
DOCUMENT_EDIT(document, "Fit Animation Length", Document::ANIMATIONS, animation->fit_length());
ImGui::SetItemTooltip("The animation length will be set to the effective length of the animation.");
if (ImGui::Button(localize.get(LABEL_FIT_ANIMATION_LENGTH), widgetSize))
DOCUMENT_EDIT(document, localize.get(EDIT_FIT_ANIMATION_LENGTH), Document::ANIMATIONS,
animation->fit_length());
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FIT_ANIMATION_LENGTH));
ImGui::EndDisabled();
ImGui::SameLine();
auto frameNum = animation ? animation->frameNum : dummy_value<int>();
ImGui::SetNextItemWidth(widgetSize.x);
if (input_int_range("Animation Length", frameNum, anm2::FRAME_NUM_MIN, anm2::FRAME_NUM_MAX, STEP, STEP_FAST,
!animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, "Animation Length", Document::ANIMATIONS, animation->frameNum = frameNum);
ImGui::SetItemTooltip("Set the animation's length.");
if (input_int_range(localize.get(LABEL_ANIMATION_LENGTH), frameNum, anm2::FRAME_NUM_MIN, anm2::FRAME_NUM_MAX,
STEP, STEP_FAST, !animation ? ImGuiInputTextFlags_DisplayEmptyRefVal : 0))
DOCUMENT_EDIT(document, localize.get(EDIT_ANIMATION_LENGTH), Document::ANIMATIONS,
animation->frameNum = frameNum);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ANIMATION_LENGTH));
ImGui::SameLine();
auto isLoop = animation ? animation->isLoop : dummy_value<bool>();
ImGui::SetNextItemWidth(widgetSize.x);
if (ImGui::Checkbox("Loop", &isLoop))
DOCUMENT_EDIT(document, "Loop", Document::ANIMATIONS, animation->isLoop = isLoop);
ImGui::SetItemTooltip("Toggle the animation looping.");
if (ImGui::Checkbox(localize.get(LABEL_LOOP), &isLoop))
DOCUMENT_EDIT(document, localize.get(EDIT_LOOP), Document::ANIMATIONS, animation->isLoop = isLoop);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LOOP_ANIMATION));
}
ImGui::EndDisabled();
@@ -1669,23 +1802,23 @@ namespace anm2ed::imgui
auto fps = anm2.info.fps;
ImGui::SetNextItemWidth(widgetSize.x);
if (input_int_range("FPS", fps, anm2::FPS_MIN, anm2::FPS_MAX))
DOCUMENT_EDIT(document, "FPS", Document::ANIMATIONS, anm2.info.fps = fps);
ImGui::SetItemTooltip("Set the FPS of all animations.");
if (input_int_range(localize.get(LABEL_FPS), fps, anm2::FPS_MIN, anm2::FPS_MAX))
DOCUMENT_EDIT(document, localize.get(EDIT_FPS), Document::ANIMATIONS, anm2.info.fps = fps);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_FPS));
ImGui::SameLine();
auto createdBy = anm2.info.createdBy;
ImGui::SetNextItemWidth(widgetSize.x);
if (input_text_string("Author", &createdBy))
DOCUMENT_EDIT(document, "Author", Document::ANIMATIONS, anm2.info.createdBy = createdBy);
ImGui::SetItemTooltip("Set the author of the document.");
if (input_text_string(localize.get(LABEL_AUTHOR), &createdBy))
DOCUMENT_EDIT(document, localize.get(EDIT_AUTHOR), Document::ANIMATIONS, anm2.info.createdBy = createdBy);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_AUTHOR));
ImGui::SameLine();
ImGui::SetNextItemWidth(widgetSize.x);
ImGui::Checkbox("Sound", &settings.timelineIsSound);
ImGui::SetItemTooltip("Toggle sounds playing with triggers.\nBind sounds to events in the Events window.");
ImGui::Checkbox(localize.get(LABEL_SOUND), &settings.timelineIsSound);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_SOUND));
ImGui::PopStyleVar();
}
@@ -1695,7 +1828,7 @@ namespace anm2ed::imgui
};
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
if (ImGui::Begin("Timeline", &settings.windowIsTimeline))
if (ImGui::Begin(localize.get(LABEL_TIMELINE_WINDOW), &settings.windowIsTimeline))
{
isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
frames_child();
@@ -1706,7 +1839,7 @@ namespace anm2ed::imgui
propertiesPopup.trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(propertiesPopup.label(), &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto item_properties_close = [&]()
{
@@ -1722,80 +1855,79 @@ namespace anm2ed::imgui
auto optionsSize = child_size_get(11);
auto itemsSize = ImVec2(0, ImGui::GetContentRegionAvail().y -
(optionsSize.y + footerSize.y + ImGui::GetStyle().ItemSpacing.y * 4));
if (ImGui::BeginChild("Options", optionsSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Options", optionsSize, ImGuiChildFlags_Borders))
{
ImGui::SeparatorText("Type");
ImGui::SeparatorText(localize.get(LABEL_TYPE));
auto size = ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, ImGui::GetFrameHeightWithSpacing());
if (ImGui::BeginChild("Type Layer", size))
if (ImGui::BeginChild("##Type 1", size))
{
ImGui::RadioButton("Layer", &type, anm2::LAYER);
ImGui::SetItemTooltip("Layers are a basic visual element in an animation, used for displaying spritesheets.");
ImGui::RadioButton(localize.get(LABEL_LAYER), &type, anm2::LAYER);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LAYER_TYPE));
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Type Null", size))
if (ImGui::BeginChild("##Type 2", size))
{
ImGui::RadioButton("Null", &type, anm2::NULL_);
ImGui::SetItemTooltip(
"Nulls are invisible elements in an animation, used for interfacing with a game engine.");
ImGui::RadioButton(localize.get(LABEL_NULL), &type, anm2::NULL_);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_NULL_TYPE));
}
ImGui::EndChild();
ImGui::SeparatorText("Source");
ImGui::SeparatorText(localize.get(LABEL_SOURCE));
if (ImGui::BeginChild("Source New", size))
if (ImGui::BeginChild("##Source 1", size))
{
ImGui::RadioButton("New", &source, source::NEW);
ImGui::SetItemTooltip("Create a new item to be used.");
ImGui::RadioButton(localize.get(LABEL_NEW), &source, source::NEW);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_NEW_ITEM));
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Source Existing", size))
if (ImGui::BeginChild("##Source 2", size))
{
auto hasUnusedItems = animation && !unused_items_get((anm2::Type)type).empty();
ImGui::BeginDisabled(!hasUnusedItems);
ImGui::RadioButton("Existing", &source, source::EXISTING);
auto isUnusedItems = animation && !unused_items_get((anm2::Type)type).empty();
ImGui::BeginDisabled(!isUnusedItems);
ImGui::RadioButton(localize.get(LABEL_EXISTING), &source, source::EXISTING);
ImGui::EndDisabled();
auto tooltip =
hasUnusedItems ? "Use a pre-existing, presently unused item." : "No unused items are available.";
ImGui::SetItemTooltip("%s", tooltip);
ImGui::SetItemTooltip("%s", isUnusedItems ? localize.get(TOOLTIP_USE_EXISTING_ITEM)
: localize.get(TOOLTIP_NO_UNUSED_ITEMS));
}
ImGui::EndChild();
ImGui::SeparatorText("Locale");
ImGui::SeparatorText(localize.get(LABEL_LOCALE));
if (ImGui::BeginChild("Locale Global", size))
if (ImGui::BeginChild("##Locale 1", size))
{
ImGui::RadioButton("Global", &locale, locale::GLOBAL);
ImGui::SetItemTooltip("The item will be inserted into all animations, if not already present.");
ImGui::RadioButton(localize.get(LABEL_GLOBAL), &locale, locale::GLOBAL);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_GLOBAL_LOCALE));
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Locale Local", size))
if (ImGui::BeginChild("##Locale 2", size))
{
ImGui::RadioButton("Local", &locale, locale::LOCAL);
ImGui::SetItemTooltip("The item will only be inserted into this animation.");
ImGui::RadioButton(localize.get(LABEL_LOCAL), &locale, locale::LOCAL);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LOCAL_LOCALE));
}
ImGui::EndChild();
ImGui::SeparatorText("Options");
ImGui::SeparatorText(localize.get(LABEL_OPTIONS));
ImGui::BeginDisabled(source == source::EXISTING);
{
input_text_string("Name", &addItemName);
ImGui::SetItemTooltip("Set the item's name.");
input_text_string(localize.get(BASIC_NAME), &addItemName);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ITEM_NAME));
ImGui::BeginDisabled(type != anm2::LAYER);
{
combo_negative_one_indexed("Spritesheet", &addItemSpritesheetID, document.spritesheet.labels);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
combo_negative_one_indexed(localize.get(LABEL_SPRITESHEET), &addItemSpritesheetID,
document.spritesheet.labels);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_LAYER_SPRITESHEET));
}
ImGui::EndDisabled();
}
@@ -1803,7 +1935,7 @@ namespace anm2ed::imgui
}
ImGui::EndChild();
if (ImGui::BeginChild("Items", itemsSize, ImGuiChildFlags_Borders))
if (ImGui::BeginChild("##Items", itemsSize, ImGuiChildFlags_Borders))
{
if (animation && source == source::EXISTING)
{
@@ -1819,15 +1951,15 @@ namespace anm2ed::imgui
if (type == anm2::LAYER)
{
auto& layer = anm2.content.layers[id];
if (ImGui::Selectable(
std::format("#{} {} (Spritesheet: #{})", id, layer.name, layer.spritesheetID).c_str(),
isSelected))
addItemID = id;
auto label =
std::vformat(localize.get(FORMAT_LAYER), std::make_format_args(id, layer.name, layer.spritesheetID));
if (ImGui::Selectable(label.c_str(), isSelected)) addItemID = id;
}
else if (type == anm2::NULL_)
{
auto& null = anm2.content.nulls[id];
if (ImGui::Selectable(std::format("#{} {}", id, null.name).c_str(), isSelected)) addItemID = id;
auto label = std::vformat(localize.get(FORMAT_NULL), std::make_format_args(id, null.name));
if (ImGui::Selectable(label.c_str(), isSelected)) addItemID = id;
}
ImGui::PopID();
@@ -1839,11 +1971,11 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
ImGui::BeginDisabled(source == source::EXISTING && addItemID == -1);
if (ImGui::Button("Add", widgetSize))
if (ImGui::Button(localize.get(BASIC_ADD), widgetSize))
{
anm2::Reference addReference{};
document.snapshot("Add Item");
document.snapshot(localize.get(EDIT_ADD_ITEM));
if (type == anm2::LAYER)
addReference = anm2.layer_animation_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName,
addItemSpritesheetID - 1, (locale::Type)locale);
@@ -1864,19 +1996,19 @@ namespace anm2ed::imgui
item_properties_close();
}
ImGui::EndDisabled();
ImGui::SetItemTooltip("Add the item, with the settings specified.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ADD_ITEM));
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) item_properties_close();
ImGui::SetItemTooltip("Cancel adding an item.");
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) item_properties_close();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CANCEL_ADD_ITEM));
ImGui::EndPopup();
}
bakePopup.trigger();
if (ImGui::BeginPopupModal(bakePopup.label, &bakePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(bakePopup.label(), &bakePopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto& interval = settings.bakeInterval;
auto& isRoundRotation = settings.bakeIsRoundRotation;
@@ -1884,19 +2016,19 @@ namespace anm2ed::imgui
auto frame = document.frame_get();
input_int_range("Interval", interval, anm2::FRAME_DURATION_MIN,
input_int_range(localize.get(LABEL_INTERVAL), interval, anm2::FRAME_DURATION_MIN,
frame ? frame->duration : anm2::FRAME_DURATION_MIN);
ImGui::SetItemTooltip("Set the maximum duration of each frame that will be baked.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_INTERVAL));
ImGui::Checkbox("Round Rotation", &isRoundRotation);
ImGui::SetItemTooltip("Rotation will be rounded to the nearest whole number.");
ImGui::Checkbox(localize.get(LABEL_ROUND_ROTATION), &isRoundRotation);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROUND_ROTATION));
ImGui::Checkbox("Round Scale", &isRoundScale);
ImGui::SetItemTooltip("Scale will be rounded to the nearest whole number.");
ImGui::Checkbox(localize.get(LABEL_ROUND_SCALE), &isRoundScale);
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ROUND_SCALE));
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Bake", widgetSize))
if (ImGui::Button(localize.get(LABEL_BAKE), widgetSize))
{
auto frames_bake = [&]()
{
@@ -1907,16 +2039,16 @@ namespace anm2ed::imgui
frames.clear();
};
DOCUMENT_EDIT(document, "Bake Frames", Document::FRAMES, frames_bake());
DOCUMENT_EDIT(document, localize.get(EDIT_BAKE_FRAMES), Document::FRAMES, frames_bake());
bakePopup.close();
}
ImGui::SetItemTooltip("Bake the selected frame(s) with the options selected.");
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_BAKE_FRAMES_OPTIONS));
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) bakePopup.close();
ImGui::SetItemTooltip("Cancel baking frames.");
if (ImGui::Button(localize.get(BASIC_CANCEL), widgetSize)) bakePopup.close();
ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CANCEL_BAKE_FRAMES));
ImGui::EndPopup();
}
@@ -1940,7 +2072,7 @@ namespace anm2ed::imgui
static bool isShortenChordHeld = false;
auto isShortenFrame = shortcut(manager.chords[SHORTCUT_SHORTEN_FRAME], shortcut::GLOBAL);
if (isShortenFrame && !isShortenChordHeld) document.snapshot("Shorten Frame");
if (isShortenFrame && !isShortenChordHeld) document.snapshot(localize.get(EDIT_SHORTEN_FRAME));
if (isShortenFrame)
{
@@ -1954,7 +2086,7 @@ namespace anm2ed::imgui
static bool isExtendChordHeld = false;
auto isExtendFrame = shortcut(manager.chords[SHORTCUT_EXTEND_FRAME], shortcut::GLOBAL);
if (isExtendFrame && !isExtendChordHeld) document.snapshot("Extend Frame");
if (isExtendFrame && !isExtendChordHeld) document.snapshot(localize.get(EDIT_EXTEND_FRAME));
if (isExtendFrame)
{

View File

@@ -1,12 +1,12 @@
#pragma once
#include <set>
#include <vector>
#include "clipboard.h"
#include "manager.h"
#include "resources.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed::imgui
{
@@ -23,9 +23,9 @@ namespace anm2ed::imgui
bool isDragging{};
bool isWindowHovered{};
bool isHorizontalScroll{};
PopupHelper propertiesPopup{PopupHelper("Item Properties", POPUP_NORMAL)};
PopupHelper bakePopup{PopupHelper("Bake", POPUP_SMALL_NO_HEIGHT)};
std::string addItemName{};
PopupHelper propertiesPopup{PopupHelper(LABEL_TIMELINE_PROPERTIES_POPUP, POPUP_NORMAL)};
PopupHelper bakePopup{PopupHelper(LABEL_TIMELINE_BAKE_POPUP, POPUP_SMALL_NO_HEIGHT)};
std::string addItemName{"New Item"};
bool addItemIsRect{};
int addItemID{-1};
int addItemSpritesheetID{-1};

View File

@@ -3,6 +3,7 @@
#include <glm/gtc/type_ptr.hpp>
#include "strings.h"
#include "tool.h"
#include "types.h"
@@ -16,7 +17,7 @@ namespace anm2ed::imgui
{
auto& document = *manager.get();
if (ImGui::Begin("Tools", &settings.windowIsTools))
if (ImGui::Begin(localize.get(LABEL_TOOLS_WINDOW), &settings.windowIsTools))
{
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2));
@@ -54,10 +55,11 @@ namespace anm2ed::imgui
if (shortcut(manager.chords[info.shortcut], shortcut::GLOBAL)) tool_use((tool::Type)i);
auto labelText = info.label;
if (i == tool::COLOR)
{
size += to_vec2(ImGui::GetStyle().FramePadding) * 2.0f;
if (ImGui::ColorButton(info.label, to_imvec4(settings.toolColor), ImGuiColorEditFlags_NoTooltip,
if (ImGui::ColorButton(labelText, to_imvec4(settings.toolColor), ImGuiColorEditFlags_NoTooltip,
to_imvec2(size)))
tool_use((tool::Type)i);
@@ -67,7 +69,7 @@ namespace anm2ed::imgui
{
if (i == tool::UNDO) ImGui::BeginDisabled(!document.is_able_to_undo());
if (i == tool::REDO) ImGui::BeginDisabled(!document.is_able_to_redo());
if (ImGui::ImageButton(info.label, resources.icons[info.icon].id, to_imvec2(size), ImVec2(0, 0), ImVec2(1, 1),
if (ImGui::ImageButton(labelText, resources.icons[info.icon].id, to_imvec2(size), ImVec2(0, 0), ImVec2(1, 1),
ImVec4(0, 0, 0, 0), iconTint))
tool_use((tool::Type)i);
if (i == tool::UNDO) ImGui::EndDisabled();
@@ -81,7 +83,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
else
usedWidth = ImGui::GetStyle().WindowPadding.x;
set_item_tooltip_shortcut(info.tooltip, settings.*SHORTCUT_MEMBERS[info.shortcut]);
set_item_tooltip_shortcut(localize.get(info.tooltip), settings.*SHORTCUT_MEMBERS[info.shortcut]);
if (isSelected) ImGui::PopStyleColor();
}
@@ -90,9 +92,9 @@ namespace anm2ed::imgui
colorEditPopup.trigger();
if (ImGui::BeginPopup(colorEditPopup.label))
if (ImGui::BeginPopup(colorEditPopup.label()))
{
ImGui::ColorPicker4(colorEditPopup.label, value_ptr(settings.toolColor));
ImGui::ColorPicker4(colorEditPopup.label(), value_ptr(settings.toolColor));
ImGui::EndPopup();
}
}

View File

@@ -3,6 +3,7 @@
#include "manager.h"
#include "resources.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed::imgui
{
@@ -11,7 +12,8 @@ namespace anm2ed::imgui
bool isOpenColorEdit{};
ImVec2 colorEditPosition{};
PopupHelper colorEditPopup{PopupHelper("##Color Edit", POPUP_TO_CONTENT, POPUP_BY_ITEM)};
PopupHelper colorEditPopup{
PopupHelper(LABEL_TOOLS_COLOR_EDIT_POPUP, POPUP_TO_CONTENT, POPUP_BY_ITEM)};
public:
void update(Manager&, Settings&, Resources&);

View File

@@ -2,6 +2,8 @@
#include <ranges>
#include "strings.h"
using namespace anm2ed::resource;
namespace anm2ed::imgui
@@ -15,24 +17,23 @@ namespace anm2ed::imgui
ImGui::SetNextWindowPos(ImVec2(viewport->Pos.x, viewport->Pos.y + taskbar.height + documents.height));
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, windowHeight));
if (ImGui::Begin("Welcome", nullptr,
if (ImGui::Begin(localize.get(LABEL_WELCOME_WINDOW), nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse))
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE_LARGE);
ImGui::TextUnformatted("Anm2Ed");
ImGui::TextUnformatted(localize.get(LABEL_APPLICATION_NAME));
ImGui::PopFont();
ImGui::TextUnformatted("Select a recent file or open a new or existing document. You can also drag and drop "
"files into the window to open them.");
ImGui::TextUnformatted(localize.get(LABEL_WELCOME_DESCRIPTION));
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("New", widgetSize)) dialog.file_save(dialog::ANM2_NEW); // handled in taskbar.cpp
if (ImGui::Button(localize.get(BASIC_NEW), widgetSize)) dialog.file_save(dialog::ANM2_NEW); // handled in taskbar.cpp
ImGui::SameLine();
if (ImGui::Button("Open", widgetSize)) dialog.file_open(dialog::ANM2_OPEN); // handled in taskbar.cpp
if (ImGui::Button(localize.get(BASIC_OPEN), widgetSize)) dialog.file_open(dialog::ANM2_OPEN); // handled in taskbar.cpp
if (ImGui::BeginChild("##Recent Files Child", {}, ImGuiChildFlags_Borders))
{
@@ -61,9 +62,9 @@ namespace anm2ed::imgui
restorePopup.trigger();
if (ImGui::BeginPopupModal(restorePopup.label, &restorePopup.isOpen, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(restorePopup.label(), &restorePopup.isOpen, ImGuiWindowFlags_NoResize))
{
ImGui::TextUnformatted("Autosaved documents detected. Would you like to restore them?");
ImGui::TextUnformatted(localize.get(LABEL_RESTORE_AUTOSAVES_PROMPT));
auto childSize = child_size_get(5);
@@ -80,7 +81,7 @@ namespace anm2ed::imgui
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Yes", widgetSize))
if (ImGui::Button(localize.get(BASIC_YES), widgetSize))
{
manager.autosave_files_open();
restorePopup.close();
@@ -88,7 +89,7 @@ namespace anm2ed::imgui
ImGui::SameLine();
if (ImGui::Button("No", widgetSize))
if (ImGui::Button(localize.get(BASIC_NO), widgetSize))
{
manager.autosave_files_clear();
restorePopup.close();

View File

@@ -2,15 +2,16 @@
#include "documents.h"
#include "manager.h"
#include "strings.h"
#include "taskbar.h"
namespace anm2ed::imgui
{
class Welcome
{
PopupHelper restorePopup{PopupHelper("Restore", imgui::POPUP_SMALL_NO_HEIGHT)};
PopupHelper restorePopup{PopupHelper(LABEL_WELCOME_RESTORE_POPUP, imgui::POPUP_SMALL_NO_HEIGHT)};
public:
void update(Manager&, Resources&, Dialog&, Taskbar&, Documents&);
};
};
};

View File

@@ -112,6 +112,7 @@ namespace anm2ed
settings = Settings(settings_path());
SnapshotStack::max_size_set(settings.fileSnapshotStackSize);
localize.language = (Language)settings.language;
if (!SDL_Init(SDL_INIT_VIDEO))
{

View File

@@ -3,8 +3,11 @@
#include <algorithm>
#include <unordered_set>
#include <format>
#include "filesystem_.h"
#include "log.h"
#include "strings.h"
#include "toast.h"
#include "vector_.h"
@@ -76,7 +79,10 @@ namespace anm2ed
if (!document.is_valid())
{
documents.pop_back();
toasts.error(std::format("Failed to open document: {} ({})", 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;
}
@@ -85,7 +91,9 @@ namespace anm2ed
selected = (int)documents.size() - 1;
pendingSelected = selected;
selection_history_push(selected);
toasts.info(std::format("Opened document: {}", pathString));
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)));
}
void Manager::new_(const std::filesystem::path& path) { open(path, true); }
@@ -96,6 +104,7 @@ namespace anm2ed
{
std::string errorString{};
document->path = !path.empty() ? path : document->path;
document->path.replace_extension(".anm2");
document->save(document->path.string(), &errorString);
recent_file_add(document->path);
}

View File

@@ -7,6 +7,7 @@
#include "document.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed
{
@@ -40,18 +41,21 @@ namespace anm2ed
std::vector<std::filesystem::path> anm2DragDropPaths{};
bool isAnm2DragDrop{};
imgui::PopupHelper anm2DragDropPopup{
imgui::PopupHelper("Anm2 Drag Drop", imgui::POPUP_NORMAL, imgui::POPUP_BY_CURSOR)};
imgui::PopupHelper(LABEL_MANAGER_ANM2_DRAG_DROP, imgui::POPUP_NORMAL, imgui::POPUP_BY_CURSOR)};
std::filesystem::path spritesheetDragDropPath{};
bool isSpritesheetDragDrop{};
anm2::Layer editLayer{};
imgui::PopupHelper layerPropertiesPopup{imgui::PopupHelper("Layer Properties", imgui::POPUP_SMALL_NO_HEIGHT)};
imgui::PopupHelper layerPropertiesPopup{
imgui::PopupHelper(LABEL_MANAGER_LAYER_PROPERTIES, imgui::POPUP_SMALL_NO_HEIGHT)};
anm2::Null editNull{};
imgui::PopupHelper nullPropertiesPopup{imgui::PopupHelper("Null Properties", imgui::POPUP_SMALL_NO_HEIGHT)};
imgui::PopupHelper nullPropertiesPopup{
imgui::PopupHelper(LABEL_MANAGER_NULL_PROPERTIES, imgui::POPUP_SMALL_NO_HEIGHT)};
imgui::PopupHelper progressPopup{imgui::PopupHelper("Rendering...", imgui::POPUP_SMALL_NO_HEIGHT)};
imgui::PopupHelper progressPopup{
imgui::PopupHelper(LABEL_MANAGER_RENDERING_PROGRESS, imgui::POPUP_SMALL_NO_HEIGHT)};
std::vector<int> selectionHistory{};

View File

@@ -11,15 +11,20 @@ namespace anm2ed::resource
pointer = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(data, length, size, &config);
}
void Font::append(void* data, size_t length, int size)
{
ImFontConfig config;
config.MergeMode = true;
config.FontDataOwnedByAtlas = false;
ImGui::GetIO().Fonts->AddFontFromMemoryTTF(data, length, size, &config);
}
Font::~Font()
{
if (get()) ImGui::GetIO().Fonts->RemoveFont(pointer);
}
ImFont* Font::get()
{
return pointer;
}
ImFont* Font::get() { return pointer; }
Font& Font::operator=(Font&& other) noexcept
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,3 @@
#include "strings.h"
namespace anm2ed
{
Language language{ENGLISH};
const char* localize(StringType type)
{
if ((language > LANGUAGE_COUNT || language < 0) || (type > STRING_TYPE_COUNT || type < 0)) return "undefined";
return LANGUAGE_STRING_COLLECTIONS[language][type];
}
}
anm2ed::Localizer localize;

View File

@@ -2,254 +2,566 @@
namespace anm2ed
{
#define SELECT_ENGLISH(symbol, english, spanish, russian, turkish, chinese, korean) english,
#define SELECT_SPANISH(symbol, english, spanish, russian, turkish, chinese, korean) spanish,
#define SELECT_RUSSIAN(symbol, english, spanish, russian, turkish, chinese, korean) russian,
#define SELECT_TURKISH(symbol, english, spanish, russian, turkish, chinese, korean) turkish,
#define SELECT_CHINESE(symbol, english, spanish, russian, turkish, chinese, korean) chinese,
#define SELECT_KOREAN(symbol, english, spanish, russian, turkish, chinese, korean) korean,
#define SELECT_ENGLISH(symbol, english, russian, korean) english,
#define SELECT_RUSSIAN(symbol, english, russian, korean) russian,
#define SELECT_KOREAN(symbol, english, russian, korean) korean,
#define LANGUAGES(X) \
X(0, ENGLISH, "English", SELECT_ENGLISH) \
X(1, SPANISH, "Spanish", SELECT_SPANISH) \
X(2, RUSSIAN, "Russian", SELECT_RUSSIAN) \
X(3, TURKISH, "Turkish", SELECT_TURKISH) \
X(4, CHINESE, "Chinese", SELECT_CHINESE) \
X(5, KOREAN, "Korean", SELECT_KOREAN)
X(ENGLISH, "English", SELECT_ENGLISH) \
X(RUSSIAN, "Pусский (Russian)", SELECT_RUSSIAN) \
X(KOREAN, "한국어 (Korean)", SELECT_KOREAN)
enum Language
{
#define X(idx, symbol, label, selector) symbol = idx,
#define X(symbol, label, selector) symbol,
LANGUAGES(X)
#undef X
LANGUAGE_COUNT
};
static constexpr const char* LANGUAGE_STRINGS[LANGUAGE_COUNT] = {
#define X(idx, symbol, label, selector) label,
#define X(symbol, label, selector) label,
LANGUAGES(X)
#undef X
};
// clang-format off
#define STRINGS(X) \
X(BASIC_YES, "Yes", "", "", "", "", "") \
X(BASIC_NO, "No", "", "", "", "", "") \
X(BASIC_CONFIRM, "Confirm", "", "", "", "", "") \
X(BASIC_CANCEL, "Cancel", "", "", "", "", "") \
X(BASIC_FRAMES, "Frames", "", "", "", "", "") \
X(BASIC_COLOR, "Color", "", "", "", "", "") \
X(BASIC_ENABLED, "Enabled", "", "", "", "", "") \
X(BASIC_GRID, "Grid", "", "", "", "", "") \
X(BASIC_SIZE, "Size", "", "", "", "", "") \
X(BASIC_OFFSET, "Offset", "", "", "", "", "") \
X(BASIC_ZOOM, "Zoom", "", "", "", "", "") \
X(BASIC_ALPHA, "Alpha", "", "", "", "", "") \
X(BASIC_POSITION, "Position", "", "", "", "", "") \
X(BASIC_PIVOT, "Pivot", "", "", "", "", "") \
X(BASIC_SCALE, "Scale", "", "", "", "", "") \
X(BASIC_ROTATION, "Rotation", "", "", "", "", "") \
X(BASIC_BEFORE, "Before", "", "", "", "", "") \
X(BASIC_AFTER, "After", "", "", "", "", "") \
X(BASIC_INDEX, "Index", "", "", "", "", "") \
X(BASIC_TIME, "Time", "", "", "", "", "") \
X(BASIC_ID, "ID", "", "", "", "", "") \
X(BASIC_CUT, "Cut", "", "", "", "", "") \
X(BASIC_COPY, "Copy", "", "", "", "", "") \
X(BASIC_PASTE, "Paste", "", "", "", "", "") \
X(BASIC_NAME, "Name", "", "", "", "", "") \
X(BASIC_CROP, "Crop", "", "", "", "", "") \
X(BASIC_DURATION, "Duration", "", "", "", "", "") \
X(BASIC_TINT, "Tint", "", "", "", "", "") \
X(BASIC_COLOR_OFFSET, "Color Offset", "", "", "", "", "") \
X(BASIC_ADD, "Add", "", "", "", "", "") \
X(BASIC_DUPLICATE, "Duplicate", "", "", "", "", "") \
X(BASIC_REMOVE, "Remove", "", "", "", "", "") \
X(BASIC_DEFAULT, "Default", "", "", "", "", "") \
X(LABEL_ANIMATION_PREVIEW_WINDOW, "Animation Preview###Animation Preview", "", "", "", "", "") \
X(LABEL_ANIMATIONS_CHILD, "Animations", "", "", "", "", "") \
X(LABEL_ALT_ICONS, "Alt Icons", "", "", "", "", "") \
X(LABEL_BACKGROUND_COLOR, "Background", "", "", "", "", "") \
X(LABEL_AXES, "Axes", "", "", "", "", "") \
X(LABEL_BORDER, "Border", "", "", "", "", "") \
X(LABEL_CENTER_VIEW, "Center View", "", "", "", "", "") \
X(LABEL_FIT, "Fit", "", "", "", "", "") \
X(LABEL_OVERLAY, "Overlay", "", "", "", "", "") \
X(LABEL_PIVOTS, "Pivots", "", "", "", "", "") \
X(LABEL_ROOT_TRANSFORM, "Root Transform", "", "", "", "", "") \
X(LABEL_ONIONSKIN, "Onionskin###Onionskin", "", "", "", "", "") \
X(LABEL_SOUNDS, "Sounds###Sounds", "", "", "", "", "") \
X(LABEL_RECORDING_PROGRESS, "Once recording is complete, rendering may take some time.\\nPlease be patient...", "", \
"", "", "", "") \
X(TOAST_SOUNDS_PASTE, "Paste Sound(s)", "", "", "", "", "") \
X(TOAST_SOUNDS_PASTE_ERROR, "Failed to paste sound(s)", "", "", "", "", "") \
X(TOAST_SPRITESHEET_NO_FRAMES, "No frames captured for spritesheet export.", "", "", "", "", "") \
X(TOAST_SPRITESHEET_EMPTY, "Spritesheet export failed: captured frames are empty.", "", "", "", "", "") \
X(TOOLTIP_GRID_VISIBILITY, "Toggle the visibility of the grid.", "", "", "", "", "") \
X(TOOLTIP_GRID_COLOR, "Change the grid's color.", "", "", "", "", "") \
X(TOOLTIP_GRID_SIZE, "Change the size of all cells in the grid.", "", "", "", "", "") \
X(TOOLTIP_GRID_OFFSET, "Change the offset of the grid.", "", "", "", "", "") \
X(TOOLTIP_PREVIEW_ZOOM, "Change the zoom of the preview.", "", "", "", "", "") \
X(TOOLTIP_CENTER_VIEW, "Centers the view.", "", "", "", "", "") \
X(TOOLTIP_FIT, "Set the view to match the extent of the animation.", "", "", "", "", "") \
X(TOOLTIP_BACKGROUND_COLOR, "Change the background color.", "", "", "", "", "") \
X(TOOLTIP_AXES, "Toggle the axes' visibility.", "", "", "", "", "") \
X(TOOLTIP_AXES_COLOR, "Set the color of the axes.", "", "", "", "", "") \
X(TOOLTIP_OVERLAY, "Set an animation to be drawn over the current animation.", "", "", "", "", "") \
X(TOOLTIP_OVERLAY_ALPHA, "Set the alpha of the overlayed animation.", "", "", "", "", "") \
X(TOOLTIP_ROOT_TRANSFORM, "Root frames will transform the rest of the animation.", "", "", "", "", "") \
X(TOOLTIP_PIVOTS, "Toggle the visibility of the animation's pivots.", "", "", "", "", "") \
X(TOOLTIP_ALT_ICONS, "Toggle a different appearance of the target icons.", "", "", "", "", "") \
X(TOOLTIP_BORDER, "Toggle the visibility of borders around layers.", "", "", "", "", "") \
X(TOOLTIP_ONIONSKIN_ENABLED, "Toggle onionskinning.", "", "", "", "", "") \
X(TOOLTIP_ONIONSKIN_FRAMES, "Change the amount of frames this onionskin will use.", "", "", "", "", "") \
X(TOOLTIP_ONIONSKIN_COLOR, "Change the color this onionskin will use.", "", "", "", "", "") \
X(TOOLTIP_ONIONSKIN_TIME, "The onionskinned frames will be based on frame time.", "", "", "", "", "") \
X(TOOLTIP_ONIONSKIN_INDEX, "The onionskinned frames will be based on frame index.", "", "", "", "", "") \
X(TOOLTIP_SOUNDS_PLAY, "Click to play.", "", "", "", "", "") \
X(TOAST_EXPORT_RENDERED_FRAMES, "Exported rendered frames to:", "", "", "", "", "") \
X(TOAST_EXPORT_RENDERED_FRAMES_FAILED, "Could not export frames to:", "", "", "", "", "") \
X(TOAST_EXPORT_SPRITESHEET, "Exported spritesheet to:", "", "", "", "", "") \
X(TOAST_EXPORT_SPRITESHEET_FAILED, "Could not export spritesheet to:", "", "", "", "", "") \
X(TOAST_EXPORT_RENDERED_ANIMATION, "Exported rendered animation to:", "", "", "", "", "") \
X(TOAST_EXPORT_RENDERED_ANIMATION_FAILED, "Could not output rendered animation:", "", "", "", "", "") \
X(LABEL_ANIMATIONS_WINDOW, "Animations###Animations", "", "", "", "", "") \
X(LABEL_MERGE, "Merge", "", "", "", "", "") \
X(LABEL_CLOSE, "Close", "", "", "", "", "") \
X(LABEL_LENGTH, "Length", "", "", "", "", "") \
X(LABEL_LOOP, "Loop", "", "", "", "", "") \
X(LABEL_APPEND_FRAMES, "Append Frames", "", "", "", "", "") \
X(LABEL_PREPEND_FRAMES, "Prepend Frames", "", "", "", "", "") \
X(LABEL_REPLACE_FRAMES, "Replace Frames", "", "", "", "", "") \
X(LABEL_IGNORE_FRAMES, "Ignore Frames", "", "", "", "", "") \
X(LABEL_DELETE_ANIMATIONS_AFTER, "Delete Animations After", "", "", "", "", "") \
X(TOOLTIP_ADD_ANIMATION, "Add a new animation.", "", "", "", "", "") \
X(TOOLTIP_DUPLICATE_ANIMATION, "Duplicate the selected animation(s).", "", "", "", "", "") \
X(TOOLTIP_OPEN_MERGE_POPUP, \
"Open the merge popup.\nUsing the shortcut will merge the animations with\nthe last configured merge settings.", \
"", "", "", "", "") \
X(TOOLTIP_REMOVE_ANIMATION, "Remove the selected animation(s).", "", "", "", "", "") \
X(TOOLTIP_SET_DEFAULT_ANIMATION, "Set the selected animation as the default.", "", "", "", "", "") \
X(TOOLTIP_DELETE_ANIMATIONS_AFTER, "Delete animations after merging them.", "", "", "", "", "") \
X(TOAST_DESERIALIZE_ANIMATIONS_FAILED, "Failed to deserialize animation(s):", "", "", "", "", "") \
X(LABEL_EVENT, "Event", "", "", "", "", "") \
X(LABEL_SOUND, "Sound", "", "", "", "", "") \
X(LABEL_APPEND, "Append", "", "", "", "", "") \
X(LABEL_REPLACE, "Replace", "", "", "", "", "") \
X(EDIT_RENAME_EVENT, "Rename Event", "", "", "", "", "") \
X(EDIT_ADD_EVENT, "Add Event", "", "", "", "", "") \
X(LABEL_EVENTS_WINDOW, "Events###Events", "", "", "", "", "") \
X(LABEL_REMOVE_UNUSED_EVENTS, "Remove Unused", "", "", "", "", "") \
X(TOOLTIP_TRIGGER_EVENT, "Change the event this trigger uses.", "", "", "", "", "") \
X(TOOLTIP_TRIGGER_SOUND, "Change the sound this trigger uses.", "", "", "", "", "") \
X(TOOLTIP_TRIGGER_AT_FRAME, "Change the frame the trigger will be activated at.", "", "", "", "", "") \
X(TOOLTIP_TRIGGER_VISIBILITY, "Toggle the trigger's visibility.", "", "", "", "", "") \
X(TOOLTIP_ADD_EVENT, "Add an event.", "", "", "", "", "") \
X(TOOLTIP_REMOVE_UNUSED_EVENTS, "Remove unused events (i.e., ones not used by any trigger in any animation.)", "", \
"", "", "", "") \
X(TOAST_DESERIALIZE_EVENTS_FAILED, "Failed to deserialize event(s):", "", "", "", "", "") \
X(EDIT_TRIGGER_EVENT, "Trigger Event", "", "", "", "", "") \
X(EDIT_TRIGGER_SOUND, "Trigger Sound", "", "", "", "", "") \
X(EDIT_TRIGGER_AT_FRAME, "Trigger At Frame", "", "", "", "", "") \
X(EDIT_TRIGGER_VISIBILITY, "Trigger Visibility", "", "", "", "", "") \
X(EDIT_PASTE_EVENTS, "Paste Event(s)", "", "", "", "", "") \
X(EDIT_PASTE_LAYERS, "Paste Layer(s)", "", "", "", "", "") \
X(EDIT_REMOVE_UNUSED_EVENTS, "Remove Unused Events", "", "", "", "", "") \
X(TOOLTIP_ITEM_NAME, "Set the item's name.", "", "", "", "", "") \
X(TOOLTIP_LAYER_SPRITESHEET, "Set the layer item's spritesheet.", "", "", "", "", "") \
X(LABEL_LAYERS_WINDOW, "Layers###Layers", "", "", "", "", "") \
X(LABEL_LAYERS_CHILD, "Layers List", "", "", "", "", "") \
X(LABEL_REMOVE_UNUSED_LAYERS, "Remove Unused", "", "", "", "", "") \
X(TOOLTIP_ADD_LAYER, "Add a layer.", "", "", "", "", "") \
X(TOOLTIP_REMOVE_UNUSED_LAYERS, "Remove unused layers (i.e., ones not used in any animation.)", "", "", "", "", "") \
X(TOAST_DESERIALIZE_LAYERS_FAILED, "Failed to deserialize layer(s):", "", "", "", "", "") \
X(EDIT_ADD_LAYER, "Add Layer", "", "", "", "", "") \
X(EDIT_REMOVE_UNUSED_LAYERS, "Remove Unused Layers", "", "", "", "", "") \
X(EDIT_SET_LAYER_PROPERTIES, "Set Layer Properties", "", "", "", "", "") \
X(LABEL_FRAME_PROPERTIES_WINDOW, "Frame Properties###Frame Properties", "", "", "", "", "") \
X(BASIC_FRAME_PROPERTIES_EVENT, "Event", "", "", "", "", "") \
X(BASIC_FRAME_PROPERTIES_SOUND, "Sound", "", "", "", "", "") \
X(BASIC_FRAME_PROPERTIES_AT_FRAME, "At Frame", "", "", "", "", "") \
X(BASIC_VISIBLE, "Visible", "", "", "", "", "") \
X(BASIC_INTERPOLATED, "Interpolated", "", "", "", "", "") \
X(LABEL_CROP, "Crop", "", "", "", "", "") \
X(LABEL_TINT, "Tint", "", "", "", "", "") \
X(LABEL_COLOR_OFFSET, "Color Offset", "", "", "", "", "") \
X(LABEL_FLIP_X, "Flip X", "", "", "", "", "") \
X(LABEL_FLIP_Y, "Flip Y", "", "", "", "", "") \
X(TOOLTIP_CROP, "Change the crop position the frame uses.", "", "", "", "", "") \
X(TOOLTIP_SIZE, "Change the size of the crop the frame uses.", "", "", "", "", "") \
X(TOOLTIP_POSITION, "Change the position of the frame.", "", "", "", "", "") \
X(TOOLTIP_PIVOT, "Change the pivot of the frame; i.e., where it is centered.", "", "", "", "", "") \
X(TOOLTIP_SCALE, "Change the scale of the frame, in percent.", "", "", "", "", "") \
X(TOOLTIP_ROTATION, "Change the rotation of the frame.", "", "", "", "", "") \
X(TOOLTIP_DURATION, "Change how long the frame lasts.", "", "", "", "", "") \
X(TOOLTIP_TINT, "Change the tint of the frame.", "", "", "", "", "") \
X(TOOLTIP_COLOR_OFFSET, "Change the color added onto the frame.", "", "", "", "", "") \
X(TOOLTIP_FRAME_VISIBILITY, "Toggle the frame's visibility.", "", "", "", "", "") \
X(TOOLTIP_FRAME_INTERPOLATION, \
"Toggle the frame interpolating; i.e., blending its values into the next frame based on the time.", "", "", "", \
"", "") \
X(TOOLTIP_FLIP_X, \
"Flip the horizontal scale of the frame, to cheat mirroring the frame horizontally.\n(Note: the format does not " \
"support mirroring.)", \
"", "", "", "", "") \
X(TOOLTIP_FLIP_Y, \
"Flip the vertical scale of the frame, to cheat mirroring the frame vertically.\n(Note: the format does not " \
"support mirroring.)", \
"", "", "", "", "") \
X(EDIT_FRAME_CROP, "Frame Crop", "", "", "", "", "") \
X(EDIT_FRAME_SIZE, "Frame Size", "", "", "", "", "") \
X(EDIT_FRAME_POSITION, "Frame Position", "", "", "", "", "") \
X(EDIT_FRAME_PIVOT, "Frame Pivot", "", "", "", "", "") \
X(EDIT_FRAME_SCALE, "Frame Scale", "", "", "", "", "") \
X(EDIT_FRAME_ROTATION, "Frame Rotation", "", "", "", "", "") \
X(EDIT_FRAME_DURATION, "Frame Duration", "", "", "", "", "") \
X(EDIT_FRAME_TINT, "Frame Tint", "", "", "", "", "") \
X(EDIT_FRAME_COLOR_OFFSET, "Frame Color Offset", "", "", "", "", "") \
X(EDIT_FRAME_VISIBILITY, "Frame Visibility", "", "", "", "", "") \
X(EDIT_FRAME_INTERPOLATION, "Frame Interpolation", "", "", "", "", "") \
X(EDIT_FRAME_FLIP_X, "Frame Flip X", "", "", "", "", "") \
X(EDIT_FRAME_FLIP_Y, "Frame Flip Y", "", "", "", "", "") \
X(LABEL_ADJUST, "Adjust", "", "", "", "", "") \
X(LABEL_SUBTRACT, "Subtract", "", "", "", "", "") \
X(LABEL_MULTIPLY, "Multiply", "", "", "", "", "") \
X(LABEL_DIVIDE, "Divide", "", "", "", "", "") \
X(TOOLTIP_ADJUST, "Set the value of each specified value onto the frame's equivalent.", "", "", "", "", "") \
X(TOOLTIP_ADD_VALUES, "Add the specified values onto each frame.\\n(Boolean values will simply be set.)", "", "", \
"", "", "") \
X(TOOLTIP_SUBTRACT_VALUES, "Subtract the specified values from each frame.\\n(Boolean values will simply be set.)", \
"", "", "", "", "") \
X(TOOLTIP_MULTIPLY_VALUES, "Multiply the specified values for each frame.\\n(Boolean values will simply be set.)", \
"", "", "", "", "") \
X(TOOLTIP_DIVIDE_VALUES, "Divide the specified values for each frame.\\n(Boolean values will simply be set.)", "", \
"", "", "", "") \
X(EDIT_CHANGE_FRAME_PROPERTIES, "Change Frame Properties", "", "", "", "", "") \
X(EDIT_MOVE_ANIMATIONS, "Move Animation(s)", "", "", "", "", "") \
X(EDIT_CUT_ANIMATIONS, "Cut Animation(s)", "", "", "", "", "") \
X(EDIT_PASTE_ANIMATIONS, "Paste Animation(s)", "", "", "", "", "") \
X(EDIT_ADD_ANIMATION, "Add Animation", "", "", "", "", "") \
X(EDIT_DUPLICATE_ANIMATIONS, "Duplicate Animation(s)", "", "", "", "", "") \
X(EDIT_MERGE_ANIMATIONS, "Merge Animations", "", "", "", "", "") \
X(EDIT_REMOVE_ANIMATIONS, "Remove Animation(s)", "", "", "", "", "") \
X(EDIT_DEFAULT_ANIMATION, "Default Animation", "", "", "", "", "") \
X(SNAPSHOT_RENAME_ANIMATION, "Rename Animation", "", "", "", "", "") \
X(LABEL_SPRITESHEET, "Spritesheet", "", "", "", "", "") \
X(LABEL_NULLS_WINDOW, "Nulls###Nulls", "", "", "", "", "") \
X(LABEL_NULLS_CHILD, "Nulls List", "", "", "", "", "") \
X(LABEL_REMOVE_UNUSED_NULLS, "Remove Unused", "", "", "", "", "") \
X(TOOLTIP_ADD_NULL, "Add a null.", "", "", "", "", "") \
X(TOOLTIP_REMOVE_UNUSED_NULLS, "Remove unused nulls (i.e., ones not used in any animation.)", "", "", "", "", "") \
X(TOAST_DESERIALIZE_NULLS_FAILED, "Failed to deserialize null(s):", "", "", "", "", "") \
X(EDIT_PASTE_NULLS, "Paste Null(s)", "", "", "", "", "") \
X(EDIT_ADD_NULL, "Add Null", "", "", "", "", "") \
X(EDIT_SET_NULL_PROPERTIES, "Set Null Properties", "", "", "", "", "") \
X(EDIT_REMOVE_UNUSED_NULLS, "Remove Unused Nulls", "", "", "", "", "") \
X(TOOLTIP_NULL_NAME, "Set the null's name.", "", "", "", "", "") \
X(TOOLTIP_NULL_RECT, "The null will have a rectangle show around it.", "", "", "", "", "") \
X(LABEL_RECT, "Rect", "", "", "", "", "") \
X(TOOLTIP_POSITION_LABEL, "Position:", "", "", "", "", "") \
X(TOOLTIP_SCALE_LABEL, "Scale:", "", "", "", "", "") \
X(TOOLTIP_ROTATION_LABEL, "Rotation:", "", "", "", "", "")
X(STRING_UNDEFINED, "undefined", "undefined", "undefined") \
X(BASIC_ADD, "Add", "Добавить", "추가") \
X(BASIC_AFTER, "After", "После", "") \
X(BASIC_ALPHA, "Alpha", "Прозрачность", "불투명도") \
X(BASIC_APPEND, "Append", "Добавить к концу", "추가") \
X(BASIC_AT_FRAME, "At Frame", "На кадре", "트리거될 프레임") \
X(BASIC_BEFORE, "Before", "До", "") \
X(BASIC_BORDER, "Border", "Границы", "경계선") \
X(BASIC_CANCEL, "Cancel", "Отмена", "취소") \
X(BASIC_COLOR, "Color", "Цвет", "색상") \
X(BASIC_COLOR_OFFSET, "Color Offset", "Смещение цвета", "색상 오프셋") \
X(BASIC_CONFIRM, "Confirm", "Подтвердить", "확인") \
X(BASIC_COPY, "Copy", "Копировать", "복사") \
X(BASIC_CROP, "Crop", "Обрезать", "자르기") \
X(BASIC_CUT, "Cut", "Вырезать", "잘라내기") \
X(BASIC_DEFAULT, "Default", "По умолчанию", "기본값") \
X(BASIC_DUPLICATE, "Duplicate", "Дублировать", "복제") \
X(BASIC_DURATION, "Duration", "Продолжительность", "유지 시간") \
X(BASIC_ENABLED, "Enabled", "Включено", "활성화") \
X(BASIC_EVENT, "Event", "Событие", "이벤트") \
X(BASIC_FRAME, "Frame", "Кадр", "프레임") \
X(BASIC_FRAMES, "Frames", "Кадры", "프레임") \
X(BASIC_GRID, "Grid", "Сетка", "격자") \
X(BASIC_ID, "ID", "ID", "ID") \
X(BASIC_INDEX, "Index", "Индекс", "인덱스") \
X(BASIC_INTERPOLATED, "Interpolated", "Интерполировано", "매끄럽게 연결") \
X(BASIC_LAYER_ANIMATION, "Layer", "Слой", "레이어") \
X(BASIC_MODE, "Mode", "Режим", "모드") \
X(BASIC_NAME, "Name", "Имя", "이름") \
X(BASIC_NEW, "New", "Новый", "새 파일") \
X(BASIC_NO, "No", "Нет", "아니오") \
X(BASIC_NONE, "None", "Ничего", "없음") \
X(BASIC_NULL_ANIMATION, "Null", "Нуль", "Null") \
X(BASIC_OFFSET, "Offset", "Смещение", "오프셋") \
X(BASIC_OPEN, "Open", "Открыть", "열기") \
X(BASIC_PASTE, "Paste", "Вставить", "붙여넣기") \
X(BASIC_PIVOT, "Pivot", "Точка вращения", "중심점") \
X(BASIC_POSITION, "Position", "Позиция", "위치") \
X(BASIC_RELOAD, "Reload", "Перезагрузить", "다시 불러오기") \
X(BASIC_REMOVE, "Remove", "Удалить", "제거") \
X(BASIC_REMOVE_UNUSED, "Remove Unused", "Удалить неизпользуемые", "미사용 시트 제거") \
X(BASIC_REPLACE, "Replace", "Заменить", "교체") \
X(BASIC_ROOT, "Root", "Корень", "Root") \
X(BASIC_ROTATION, "Rotation", "Поворот", "회전") \
X(BASIC_SAVE, "Save", "Сохранить", "저장") \
X(BASIC_SCALE, "Scale", "Масштаб", "크기") \
X(BASIC_SIZE, "Size", "Размер", "비율") \
X(BASIC_SOUND, "Sound", "Звук", "사운드") \
X(BASIC_TIME, "Time", "Время", "시간") \
X(BASIC_TINT, "Tint", "Оттенок", "색조") \
X(BASIC_TRIGGERS, "Triggers", "Триггер", "트리거") \
X(BASIC_VISIBLE, "Visible", "Видимый", "표시") \
X(BASIC_YES, "Yes", "Да", "예") \
X(BASIC_ZOOM, "Zoom", "Масштаб", "확대") \
X(EDIT_ADD_ANIMATION, "Add Animation", "Добавить анимацию", "애니메이션 추가") \
X(EDIT_ADD_EVENT, "Add Event", "Добавить событие", "이벤트 추가") \
X(EDIT_ADD_ITEM, "Add Item", "Добавить предмет", "항목 추가") \
X(EDIT_ADD_LAYER, "Add Layer", "Добавить слой", "레이어 추가") \
X(EDIT_ADD_NULL, "Add Null", "Добавить нуль", "Null 추가") \
X(EDIT_ADD_SPRITESHEET, "Add Spritesheet", "Добавить спрайт-лист", "스프라이트 시트 추가") \
X(EDIT_ADD_SOUND, "Add Sound", "Добавить звук", "사운드 추가") \
X(EDIT_ANIMATION_LENGTH, "Animation Length", "Длина анимации", "애니메이션 길이") \
X(EDIT_AUTHOR, "Author", "Автор", "작성자") \
X(EDIT_BAKE_FRAMES, "Bake Frames", "Запечь кадры", "프레임 베이크") \
X(EDIT_CHANGE_FRAME_PROPERTIES, "Change Frame Properties", "Изменить свойства кадров", "프레임 속성 변경") \
X(EDIT_CUT_ANIMATIONS, "Cut Animation(s)", "Вырезать анимации", "애니메이션 잘라내기") \
X(EDIT_CUT_FRAMES, "Cut Frame(s)", "Вырезать кадры", "프레임 잘라내기") \
X(EDIT_DEFAULT_ANIMATION, "Default Animation", "Анимация по умолчанию", "기본 애니메이션") \
X(EDIT_DELETE_FRAMES, "Delete Frame(s)", "Удалить кадры", "프레임 삭제하기") \
X(EDIT_DRAW, "Draw", "Рисовать", "그리기") \
X(EDIT_DUPLICATE_ANIMATIONS, "Duplicate Animation(s)", "Дублировать кадры", "애니메이션 복제") \
X(EDIT_ERASE, "Erase", "Стереть", "지우기") \
X(EDIT_EXTEND_FRAME, "Extend Frame", "Удлиннить кадр", "프레임 확장") \
X(EDIT_FIT_ANIMATION_LENGTH, "Fit Animation Length", "Подогнать к длине анимации", "애니메이션 길이 맞추기") \
X(EDIT_FPS, "FPS", "FPS", "FPS") \
X(EDIT_FRAME_COLOR_OFFSET, "Frame Color Offset", "Смещение цвета кадра", "프레임 색상 오프셋") \
X(EDIT_FRAME_CROP, "Frame Crop", "Обрезка кадра", "프레임 자르기") \
X(EDIT_FRAME_DURATION, "Frame Duration", "Продолжительность кадра", "프레임 유지 시간") \
X(EDIT_FRAME_FLIP_X, "Frame Flip X", "Отразить кадр по X", "프레임 수평 뒤집기") \
X(EDIT_FRAME_FLIP_Y, "Frame Flip Y", "Отразить кадр по Y", "프레임 수직 뒤집기") \
X(EDIT_FRAME_INTERPOLATION, "Frame Interpolation", "Интерполяция кадра", "매끄럽게 프레임 연결") \
X(EDIT_FRAME_PIVOT, "Frame Pivot", "Точка вращения кадра", "프레임 중심점") \
X(EDIT_FRAME_POSITION, "Frame Position", "Позиция кадра", "프레임 위치") \
X(EDIT_FRAME_ROTATION, "Frame Rotation", "Поворот кадра", "프레임 회전") \
X(EDIT_FRAME_SCALE, "Frame Scale", "Масштаб кадра", "프레임 비율") \
X(EDIT_FRAME_SIZE, "Frame Size", "Размер кадра", "프레임 크기") \
X(EDIT_FRAME_TINT, "Frame Tint", "Оттенок кадра", "프레임 색조") \
X(EDIT_FRAME_VISIBILITY, "Frame Visibility", "Видимость кадра", "프레임 표시") \
X(EDIT_GENERATE_ANIMATION_FROM_GRID, "Generate Animation from Grid", "Создать анимацию с сетки", "격자로 애니메이션 생성") \
X(EDIT_INSERT_FRAME, "Insert Frame", "Вставить кадр", "프레임 삽입하기") \
X(EDIT_LOOP, "Loop", "Цикл", "반복") \
X(EDIT_MERGE_ANIMATIONS, "Merge Animations", "Соединить анимации", "애니메이션 병합") \
X(EDIT_MERGE_ANM2, "Merge Anm2", "Соединить Anm2", "Anm2 병합") \
X(EDIT_MOVE_ANIMATIONS, "Move Animation(s)", "Переместить анимации", "애니메이션 이동") \
X(EDIT_MOVE_FRAMES, "Move Frame(s)", "Перемесить кадры", "프레임 이동") \
X(EDIT_MOVE_LAYER_ANIMATION, "Move Layer Animation", "Переместить анимацию слоя", "레이어 애니메이션 이동") \
X(EDIT_PASTE_ANIMATIONS, "Paste Animation(s)", "Вставить анимации", "애니메이션 붙여넣기") \
X(EDIT_PASTE_EVENTS, "Paste Event(s)", "Вставить события", "이벤트 붙여넣기") \
X(EDIT_PASTE_FRAMES, "Paste Frame(s)", "Вставить кадры", "프레임 붙여넣기") \
X(EDIT_PASTE_LAYERS, "Paste Layer(s)", "Вставить слои", "레이어 붙여넣기") \
X(EDIT_PASTE_NULLS, "Paste Null(s)", "Вставить нули", "Null 붙여넣기") \
X(EDIT_PASTE_SPRITESHEETS, "Paste Spritesheet(s)", "Вставить спрайт-листы", "스프라이트 시트 붙여넣기") \
X(EDIT_RELOAD_SPRITESHEETS, "Reload Spritesheet(s)", "Перезагрузить спрайт-листы", "스프라이트 시트 다시 불러오기") \
X(EDIT_REMOVE_ANIMATIONS, "Remove Animation(s)", "Удалить анимации", "애니메이션 제거") \
X(EDIT_REMOVE_ITEMS, "Remove Item(s)", "Удалить предметы", "항목 제거") \
X(EDIT_REMOVE_UNUSED_EVENTS, "Remove Unused Events", "Удалить неизпользуемые события", "미사용 이벤트 제거") \
X(EDIT_REMOVE_UNUSED_LAYERS, "Remove Unused Layers", "Удалить неизпользуемые слои", "미사용 레이어 제거") \
X(EDIT_REMOVE_UNUSED_NULLS, "Remove Unused Nulls", "Удалить неизпользуемые нули", "미사용 Null 제거") \
X(EDIT_REMOVE_UNUSED_SOUNDS, "Remove Unused Sounds", "Удалить неизпользуемые звуки", "미사용 사운드 제거") \
X(EDIT_REMOVE_UNUSED_SPRITESHEETS, "Remove Unused Spritesheets", "Удалить неизпользуемые спрайт-листы", "미사용 스프라이트 시트 제거") \
X(EDIT_RENAME_EVENT, "Rename Event", "Переименовать событие", "이벤트 이름 바꾸기") \
X(EDIT_REPLACE_SPRITESHEET, "Replace Spritesheet", "Заменить спрайт-лист", "스프라이트 시트 교체") \
X(EDIT_SET_LAYER_PROPERTIES, "Set Layer Properties", "Установить свойства слоя", "레이어 속성 설정") \
X(EDIT_SET_NULL_PROPERTIES, "Set Null Properties", "Установить свойства нуля", "Null 속성 설정") \
X(EDIT_SHORTEN_FRAME, "Shorten Frame", "Укоротить кадр", "프레임 단축") \
X(EDIT_TOGGLE_ITEM_VISIBILITY, "Toggle Item Visibility", "Переключить видимость предмета", "항목 표시/숨기기") \
X(EDIT_TOGGLE_NULL_RECT, "Toggle Null Rectangle", "Переключить прямоугольник нуля", "Null 사각형 표시/숨기기") \
X(EDIT_TRIGGER_AT_FRAME, "Trigger At Frame", "Активировать на кадре", "트리거 프레임") \
X(EDIT_TRIGGER_EVENT, "Trigger Event", "Событие триггера", "트리거 이벤트") \
X(EDIT_TRIGGER_SOUND, "Trigger Sound", "Звук триггера", "트리거 사운드") \
X(EDIT_TRIGGER_VISIBILITY, "Trigger Visibility", "Видимость триггера", "트리거 표시") \
X(FORMAT_AT_FRAME, "At Frame: {0}", "На кадре: {0}", "트리거될 프레임: {0}") \
X(FORMAT_COLOR_OFFSET, "Color Offset: {0}, {1}, {2}", "Смещение цвета: {0} {1} {2}", "색상 오프셋: {0} {1} {2}") \
X(FORMAT_CROP, "Crop: ({0}, {1})", "Обрезка: ({0}, {1})", "자르기: ({0}, {1})") \
X(FORMAT_DURATION, "Duration: {0}", "Продолжительность: {0}", "유지 시간: {0}") \
X(FORMAT_EVENT_LABEL, "Event: {0}", "Событие: {0}", "이벤트: {0}") \
X(FORMAT_ID, "ID: {0}", "ID: {0}", "ID: {0}") \
X(FORMAT_SPRITESHEET_ID, "Spritesheet ID: {0}", "", "스프라이트 시트 ID: {0}") \
X(FORMAT_INDEX, "Index: {0}", "Индекс: {0}", "인덱스: {0}") \
X(FORMAT_INTERPOLATED, "Interpolated: {0}", "Интерполировано: {0}", "매끄럽게 연결: {0}") \
X(FORMAT_LAYER, "#{0} {1} (Spritesheet: #{2})", "#{0} {1} (Спрайт-лист: #{2})", "#{0} {1} (스프라이트 시트: #{2})") \
X(FORMAT_LENGTH, "Length: {0}", "Длина: {0}", "길이: {0}") \
X(FORMAT_LOOP, "Loop: {0}", "Цикл: {0}", "반복: {0}") \
X(FORMAT_NOT_SAVED, "{0} [Not Saved]", "{0} [Не сохранено]", "{0} [저장되지 않음]") \
X(FORMAT_NULL, "#{0} {1}", "#{0} {1}", "#{0} {1}") \
X(FORMAT_PIVOT, "Pivot: ({0}, {1})", "Точка вращения: ({0}, {1})", "중심점: ({0}, {1})") \
X(FORMAT_POSITION, "Position: ({0}, {1})", "Позиция: ({0}, {1})", "위치: ({0}, {1})") \
X(FORMAT_POSITION_SPACED, "Position: ({0:8}, {1:8})", "Позиция: ({0:8}, {1:8})", "위치: ({0:8}, {1:8})") \
X(FORMAT_ROTATION, "Rotation: {0}", "Поворот: {0}", "회전: {0}") \
X(FORMAT_SCALE, "Scale: ({0}, {1})", "Масштаб: ({0}, {1})", "비율: ({0}, {1})") \
X(FORMAT_SIZE, "Size: ({0}, {1})", "Размер: ({0}, {1})", "크기: ({0}, {1})") \
X(FORMAT_SOUND_LABEL, "Sound: {0}", "Звук: {0}", "사운드: {0}") \
X(FORMAT_SPRITESHEET, "#{0} {1}", "#{0} {1}", "#{0} {1}") \
X(FORMAT_TRANSFORM, "Transform: {0}", "Трансформация: {0}", "변환: {0}") \
X(FORMAT_RECT, "Rect: {0}", "Прямоугольник: {0}", "사각형: {0}") \
X(FORMAT_FRAMES_COUNT, "Frames: {0}", "Кадры: {0}", "프레임: {0}") \
X(FORMAT_TRIGGERS_COUNT, "Triggers: {0}", "Триггеры: {0}", "트리거: {0}") \
X(FORMAT_TEXTURE_SIZE, "Size: {0}x{1}", "Размер: {0}x{1}", "크기: {0}x{1}") \
X(FORMAT_TINT, "Tint: {0}, {1}, {2}, {3}", "Оттенок: {0}, {1}, {2}, {3}", "색조: {0}, {1}, {2}, {3}") \
X(FORMAT_TOOLTIP_SHORTCUT, "{0}\n(Shortcut: {1})", "{0}\n(Сочетание клавиш: {1})", "{0}\n(단축키: {1})") \
X(FORMAT_VISIBLE, "Visible: {0}", "Видимо: {0}", "표시: {0}") \
X(LABEL_ADJUST, "Adjust", "Отрегулировать", "조정") \
X(LABEL_ALT_ICONS, "Alt Icons", "Альт-иконки", "대체 아이콘") \
X(LABEL_ANIMATIONS_CHILD, "Animations", "", "애니메이션") \
X(LABEL_ANIMATIONS_MERGE_POPUP, "Merge Animations", "Соединить анимации", "애니메이션 병합") \
X(LABEL_ANIMATIONS_WINDOW, "Animations###Animations", "Анимации###Animations", "애니메이션###Animations") \
X(LABEL_ANIMATION_LENGTH, "Animation Length", "Длина анимации", "애니메이션 길이") \
X(LABEL_ANIMATION_PREVIEW_WINDOW, "Animation Preview###Animation Preview", "Предпросмотр анимации###Animation Preview", "애니메이션 프리뷰###Animation Preview") \
X(LABEL_APPEND_FRAMES, "Append Frames", "Добавить кадры к концу", "뒷프레임에 추가") \
X(LABEL_APPLICATION_NAME, "Anm2Ed", "Anm2Ed", "Anm2Ed") \
X(LABEL_APPLICATION_VERSION, "Version 2.1", "Версия 2.1", "버전 2.0") \
X(LABEL_AUTHOR, "Author", "Автор", "작성자") \
X(LABEL_AUTOSAVE, "Autosave", "Автосохранение", "자동저장") \
X(LABEL_AXES, "Axes", "Оси", "가로/세로 축") \
X(LABEL_BACKGROUND_COLOR, "Background", "Фон", "배경색") \
X(LABEL_BAKE, "Bake", "Запечь", "베이크") \
X(LABEL_BORDER, "Border", "Границы", "경계선") \
X(LABEL_CENTER_VIEW, "Center View", "Центрировать вид", "가운데서 보기") \
X(LABEL_CLAMP, "Clamp", "Ограничить", "작업 영역 제한") \
X(LABEL_CLEAR_LIST, "Clear List", "Стереть список", "기록 삭제") \
X(LABEL_CLOSE, "Close", "Закрыть", "닫기") \
X(LABEL_CUSTOM_RANGE, "Custom Range", "Пользовательский диапазон", "길이 맞춤설정") \
X(LABEL_DELETE, "Delete", "Удалить", "삭제") \
X(LABEL_DELETE_ANIMATIONS_AFTER, "Delete Animations After", "Удалить анимации после", "기존 애니메이션 삭제") \
X(LABEL_DISPLAY, "Display", "Отображение", "디스플레이") \
X(LABEL_DIVIDE, "Divide", "Разделить", "나누기") \
X(LABEL_DOCUMENTS_MERGE_INTO_CURRENT, "Merge into Current Document", "Соединить в текущий документ", "현재 파일에 병합") \
X(LABEL_DOCUMENTS_OPEN_MANY, "Open Many Documents", "Открыть несколько документов", "여러 파일 열기") \
X(LABEL_DOCUMENTS_OPEN_NEW, "Open New Document", "Открыть новый документ", "새 파일로 열기") \
X(LABEL_DOCUMENT_CLOSE, "Close Document", "Закрыть документ", "파일 닫기") \
X(LABEL_DOCUMENT_MODIFIED_PROMPT, "The document \"{0}\" has been modified.\nDo you want to save it?", "Документ \"{0}\" был изменен. \nХотите сохранить его?", "\"{0}\" 파일이 수정되었습니다.\n저장하시겠습니까?") \
X(LABEL_END, "End", "Конец", "") \
X(LABEL_EVENT, "Event", "Событие", "이벤트") \
X(LABEL_EVENTS_WINDOW, "Events###Events", "События###Events", "이벤트###Events") \
X(LABEL_EXISTING, "Existing", "Существующий", "기존") \
X(LABEL_EXIT, "Exit", "Выход", "종료") \
X(LABEL_EXPLORE_XML_LOCATION, "Explore XML Location", "Открыть местоположение XML", "XML 경로로 탐색기 열기") \
X(LABEL_FFMPEG_PATH, "FFmpeg Path", "Путь к FFmpeg", "FFmpeg 경로") \
X(LABEL_FILE_MENU, "File", "Файл", "파일") \
X(LABEL_FIT, "Fit", "Подогнать по размеру", "맞추기") \
X(LABEL_FIT_ANIMATION_LENGTH, "Fit Animation Length", "Подогнать к длине анимации", "애니메이션 길이 맞추기") \
X(LABEL_FLIP_X, "Flip X", "Отразить по X", "수평 뒤집기") \
X(LABEL_FLIP_Y, "Flip Y", "Отразить по Y", "수직 뒤집기") \
X(LABEL_FORMAT, "Format", "Формат", "파일 포맷") \
X(LABEL_FPS, "FPS", "FPS", "FPS") \
X(LABEL_FRAME_PROPERTIES_WINDOW, "Frame Properties###Frame Properties", "Свойства кадра###Frame Properties", "프레임 속성###Frame Properties") \
X(LABEL_GENERATE, "Generate", "Сгенерировать", "생성") \
X(LABEL_GENERATE_COLUMNS, "Columns", "Колонны", "") \
X(LABEL_GENERATE_COUNT, "Count", "Кол-во", "프레임 수") \
X(LABEL_GENERATE_FRAME_SIZE, "Frame Size", "Размер кадра", "프레임 크기") \
X(LABEL_GENERATE_ROWS, "Rows", "Ряды", "행") \
X(LABEL_GENERATE_START_POSITION, "Start Position", "Начальная позиция", "시작 위치") \
X(LABEL_GLOBAL, "Global", "Глобальный", "전역 추가") \
X(LABEL_HELP_MENU, "Help", "Помощь", "도움말") \
X(LABEL_IGNORE_FRAMES, "Ignore Frames", "Игнорировать кадры", "프레임 무시") \
X(LABEL_INPUT, "Input", "Ввод", "입력") \
X(LABEL_INSERT, "Insert", "Вставить", "삽입") \
X(LABEL_INTERVAL, "Interval", "Промежуток", "간격") \
X(LABEL_KEYBOARD, "Keyboard", "Клавиатура", "키보드") \
X(LABEL_LANGUAGE, "Language", "Язык", "언어") \
X(LABEL_LAYER, "Layer", "Слой", "레이어") \
X(LABEL_LAYERS_CHILD, "Layers List", "", "레이어 목록") \
X(LABEL_LAYERS_WINDOW, "Layers###Layers", "Слои###Layers", "레이어###Layers") \
X(LABEL_LENGTH, "Length", "", "길이") \
X(LABEL_LOCAL, "Local", "Локальный", "지역 추가") \
X(LABEL_LOCALE, "Locale", "Локаль", "추가할 위치") \
X(LABEL_LOCALIZATION, "Localization", "Локализация", "현지화") \
X(LABEL_LOOP, "Loop", "Цикл", "반복") \
X(LABEL_MANAGER_ANM2_DRAG_DROP, "Anm2 Drag Drop", "Anm2 Drag Drop", "Anm2 드래그 앤 드롭") \
X(LABEL_MANAGER_LAYER_PROPERTIES, "Layer Properties", "Свойства слоя", "레이어 속성") \
X(LABEL_MANAGER_NULL_PROPERTIES, "Null Properties", "Свойства нуля", "Null 속성") \
X(LABEL_MANAGER_RENDERING_PROGRESS, "Rendering...", "Рендеринг...", "렌더링 중...") \
X(LABEL_MERGE, "Merge", "Соединить", "병합") \
X(LABEL_MOVE_TOOL_SNAP, "Move Tool: Snap to Mouse", "Инструмент перемещения: Привязка к мыши", "이동 도구: 마우스에 맞추기") \
X(LABEL_MULTIPLY, "Multiply", "Умножить", "곱하기") \
X(LABEL_NEW, "New", "Новый", "새로") \
X(LABEL_NULL, "Null", "Нуль", "Null") \
X(LABEL_NULLS_WINDOW, "Nulls###Nulls", "Нули###Nulls", "Null###Nulls") \
X(LABEL_ONIONSKIN_WINDOW, "Onionskin###Onionskin", "Оньонскин###Onionskin", "전후 비교###Onionskin") \
X(LABEL_OPEN_RECENT, "Open Recent", "Открыть недавние", "최근 파일 열기") \
X(LABEL_OPTIONS, "Options", "Настройки", "설정") \
X(LABEL_OUTPUT_DIRECTORY, "Directory", "Директория", "디렉터리") \
X(LABEL_OUTPUT_PATH, "Path", "Путь", "경로") \
X(LABEL_OVERLAY, "Overlay", "Наложение", "오버레이") \
X(LABEL_OVERWRITE_CONFIRMATION, "Are you sure? This will overwrite the existing file.", "Вы уверены? Это перезапишет существующий файл.", "저장하시겠습니까? 기존 파일을 덮어쓰게 됩니다.") \
X(LABEL_OVERWRITE_WARNING, "Overwrite Warning", "Предупреждение о перезаписи", "덮어쓰기 경고") \
X(LABEL_PAUSE, "Pause", "Пауза", "일시정지") \
X(LABEL_PIVOTS, "Pivots", "Точки вращения", "중심점") \
X(LABEL_PLAY, "Play", "Возпроизвести", "재생") \
X(LABEL_PLAYBACK_ALWAYS_LOOP, "Always Loop", "Всегда воспроизводить циклично", "항상 반복") \
X(LABEL_PLAYBACK_MENU, "Playback", "Возпроизведение", "재생") \
X(LABEL_PREPEND_FRAMES, "Prepend Frames", "Добавить кадры", "뒷프레임에 추가") \
X(LABEL_RAW, "Raw", "Необработанный", "Raw만") \
X(LABEL_RECT, "Rect", "Прямоугольник", "사각형") \
X(LABEL_REMOVE_UNUSED_LAYERS, "Remove Unused", "", "미사용 레이어 제거") \
X(LABEL_REMOVE_UNUSED_NULLS, "Remove Unused", "", "미사용 Null 제거") \
X(LABEL_REMOVE_UNUSED_SPRITESHEETS, "Remove Unused", "", "미사용 스프라이트 시트 제거") \
X(LABEL_RENDER, "Render", "Рендер", "렌더링") \
X(LABEL_REPEAT_DELAY, "Repeat Delay (seconds)", "Задержка повторения (секунды)", "반복 입력 지연 시간 (초)") \
X(LABEL_REPEAT_RATE, "Repeat Rate (seconds)", "Скорость повторения (секунды)", "반복 입력 속도 (초)") \
X(LABEL_REPLACE_FRAMES, "Replace Frames", "Заменить кадры", "프레임 교체") \
X(LABEL_RESTORE_AUTOSAVES_PROMPT, "Autosaved documents detected. Would you like to restore them?", "Автосохраненные документы найдены. Хотите их восстановить?", "자동저장된 파일이 감지되었습니다. 복원하시겠습니까?") \
X(LABEL_ROOT_TRANSFORM, "Root Transform", "Трансформация корня", "Root 변환") \
X(LABEL_ROUND_ROTATION, "Round Rotation", "Округленный поворот", "회전율 반올림") \
X(LABEL_ROUND_SCALE, "Round Scale", "Округленный масштаб", "비율 반올림") \
X(LABEL_SAVE_AS, "Save As", "Сохранить как", "다른 이름으로 저장") \
X(LABEL_SETTINGS_MENU, "Settings", "Настройки", "설정") \
X(LABEL_SET_TO_RECOMMENDED, "Set to Recommended", "Установить на рекомендованные", "권장값으로 설정") \
X(LABEL_SHORTCUTS_TAB, "Shortcuts", "Сочетания клавиш", "단축키") \
X(LABEL_SHORTCUT_COLUMN, "Shortcut", "Сочетание клавиш", "단축") \
X(LABEL_SNAP, "Snap", "Привязка", "맞추기") \
X(LABEL_SNAPSHOTS, "Snapshots", "Снапшоты", "스냅숏") \
X(LABEL_SOUND, "Sound", "Звук", "사운드") \
X(LABEL_SOUNDS_WINDOW, "Sounds###Sounds", "Звук###Sounds", "사운드###Sounds") \
X(LABEL_SOURCE, "Source", "Източник", "소스") \
X(LABEL_SPRITESHEET, "Spritesheet", "Спрайт-лист", "스프라이트 시트") \
X(LABEL_SPRITESHEETS_WINDOW, "Spritesheets###Spritesheets", "Спрайт-листы###Spritesheets", "스프라이트 시트###Spritesheets") \
X(LABEL_SPRITESHEET_EDITOR_WINDOW, "Spritesheet Editor###Spritesheet Editor", "Редактор спрайт-листов###Spritesheet Editor", "스프라이트 편집기###Spritesheet Editor") \
X(LABEL_STACK_SIZE, "Stack Size", "Размер стека", "스택 크기") \
X(LABEL_START, "Start", "Старт", "시작") \
X(LABEL_SUBTRACT, "Subtract", "Вычетание", "빼기") \
X(LABEL_TASKBAR_ABOUT, "About", "Около", "정보") \
X(LABEL_TASKBAR_CONFIGURE, "Configure", "Конфигурация", "구성 설정") \
X(LABEL_TASKBAR_GENERATE_ANIMATION_FROM_GRID, "Generate Animation from Grid", "Создать анимацию с сетки", "격자로 애니메이션 생성") \
X(LABEL_TASKBAR_OVERWRITE_FILE, "Overwrite File", "Перезапись файла", "파일 덮어쓰기") \
X(LABEL_TASKBAR_RENDER_ANIMATION, "Render Animation", "Рендер анимации", "애니메이션 렌더링") \
X(LABEL_THEME, "Theme", "Тема", "테마") \
X(LABEL_THEME_CLASSIC, "ImGui Classic", "Классическая ImGui", "ImGui 클래식") \
X(LABEL_THEME_DARK, "Dark", "Темная", "어둡게") \
X(LABEL_THEME_LIGHT, "Light", "Светлая", "밝게") \
X(LABEL_TIMELINE_BAKE_POPUP, "Bake", "Запечь", "베이크") \
X(LABEL_TIMELINE_PROPERTIES_POPUP, "Item Properties", "Свойства предмета", "항목 속성") \
X(LABEL_TIMELINE_WINDOW, "Timeline###Timeline", "Таймлайн###Timeline", "타임라인###Timeline") \
X(LABEL_TIME_MINUTES, "Time (minutes)", "Время (минуты)", "시간 (분)") \
X(LABEL_TOOL, "Tool", "Инструмент", "도구") \
X(LABEL_TOOLS_COLOR_EDIT_POPUP, "##Color Edit", "##Color Edit", "##Color Edit") \
X(LABEL_TOOLS_WINDOW, "Tools###Tools", "Инструменты###Tools", "도구###Tools") \
X(LABEL_TO_ANIMATION_RANGE, "To Animation Range", "К диапазону анимации", "애니메이션 범위에 맞춤") \
X(LABEL_TO_SELECTED_FRAMES, "To Selected Frames", "К выбранным кадрам", "선택한 프레임에 맞춤") \
X(LABEL_TRANSPARENT, "Transparent", "Прозрачный", "투명도") \
X(LABEL_TYPE, "Type", "Тип", "유형") \
X(LABEL_UI_SCALE, "UI Scale", "Размер UI", "UI 비율") \
X(LABEL_USE_DEFAULT_SETTINGS, "Use Default Settings", "Изпользовать настройки по умолчанию", "기본값으로 사용") \
X(LABEL_VALUE_COLUMN, "Value", "Значение", "값") \
X(LABEL_VSYNC, "Vsync", "Вертикальная синхронизация (V-sync)", "수직 동기화") \
X(LABEL_WELCOME_DESCRIPTION, "Select a recent file or open a new or existing document. You can also drag and drop files into the window to open them.", "Выберите недавний файл или откройте новый или существующий документ. Вы также можете перетащить файлы в это окно, чтобы их открыть.", "최근 파일을 선택하거나 새 파일 또는 기존 파일을 엽니다. 파일을 여기로 드래그하여 열 수 있습니다.") \
X(LABEL_WELCOME_RESTORE_POPUP, "Restore", "Восстановиь", "복원") \
X(LABEL_WELCOME_WINDOW, "Welcome###Welcome", "Добро пожаловать###Welcome", "환영합니다###Welcome") \
X(LABEL_WINDOW_MENU, "Window", "Окно", "창") \
X(LABEL_WIZARD_MENU, "Wizard", "Помощник", "보조 도구") \
X(LABEL_ZOOM, "Zoom", "Масштаб", "확대") \
X(LABEL_ZOOM_STEP, "Step", "Шаг", "단계") \
X(SHORTCUT_STRING_ADD, "Add", "Добавить", "추가") \
X(SHORTCUT_STRING_CENTER_VIEW, "Center View", "Центрировать вид", "가운데서 보기") \
X(SHORTCUT_STRING_CLOSE, "Close", "Закрыть", "닫기") \
X(SHORTCUT_STRING_COLOR, "Color", "Цвет", "색상") \
X(SHORTCUT_STRING_COLOR_PICKER, "Color Picker", "Выбор цвета", "색상 선택기") \
X(SHORTCUT_STRING_COPY, "Copy", "Копировать", "복사") \
X(SHORTCUT_STRING_CROP, "Crop", "Обрезать", "자르기") \
X(SHORTCUT_STRING_CUT, "Cut", "Вырезать", "잘라내기") \
X(SHORTCUT_STRING_DEFAULT, "Default", "По умолчанию", "기본값") \
X(SHORTCUT_STRING_DRAW, "Draw", "Рисовать", "그리기") \
X(SHORTCUT_STRING_DUPLICATE, "Duplicate", "Дублировать", "복제") \
X(SHORTCUT_STRING_ERASE, "Erase", "Стереть", "지우기") \
X(SHORTCUT_STRING_EXIT, "Exit", "Выйти", "종료") \
X(SHORTCUT_STRING_EXTEND_FRAME, "Extend Frame", "Удлиннить кадр", "프레임 확장") \
X(SHORTCUT_STRING_FIT, "Fit", "Подогнать", "맞추기") \
X(SHORTCUT_STRING_INSERT_FRAME, "Insert Frame", "Вставить кадр", "프레임 삽입") \
X(SHORTCUT_STRING_MERGE, "Merge", "Соединить", "병합") \
X(SHORTCUT_STRING_MOVE, "Move", "Передвинуть", "이동") \
X(SHORTCUT_STRING_NEW, "New", "Новый", "새 파일") \
X(SHORTCUT_STRING_NEXT_FRAME, "Next Frame", "Следующий кадр", "다음 프레임") \
X(SHORTCUT_STRING_ONIONSKIN, "Onionskin", "Оньонскин", "전후 비교") \
X(SHORTCUT_STRING_OPEN, "Open", "Открыть", "열기") \
X(SHORTCUT_STRING_PAN, "Pan", "Панорамирование", "시점 이동") \
X(SHORTCUT_STRING_PASTE, "Paste", "Вставить", "붙여넣기") \
X(SHORTCUT_STRING_PLAYHEAD_BACK, "Playhead Back", "Назад воспроизводящей головкой", "플레이헤드 뒤로") \
X(SHORTCUT_STRING_PLAYHEAD_FORWARD, "Playhead Forward", "Вперед воспроизводящей головкой", "플레이헤드 앞으로") \
X(SHORTCUT_STRING_PLAY_PAUSE, "Play/Pause", "Возпроизвести/Пауза", "재생/일시정지") \
X(SHORTCUT_STRING_PREVIOUS_FRAME, "Previous Frame", "Предыдущий кадр", "이전 프레임") \
X(SHORTCUT_STRING_REDO, "Redo", "Повторить", "다시 실행") \
X(SHORTCUT_STRING_REMOVE, "Remove", "Удалить", "제거") \
X(SHORTCUT_STRING_ROTATE, "Rotate", "Поворачивать", "회전") \
X(SHORTCUT_STRING_SAVE, "Save", "Сохранить", "저장") \
X(SHORTCUT_STRING_SAVE_AS, "Save As", "Сохранить как", "다른 이름으로 저장") \
X(SHORTCUT_STRING_SCALE, "Scale", "Изменить масштаб", "비율") \
X(SHORTCUT_STRING_SHORTEN_FRAME, "Shorten Frame", "Укоротить кадр", "프레임 단축") \
X(SHORTCUT_STRING_UNDO, "Undo", "Отменить", "실행 취소") \
X(SHORTCUT_STRING_ZOOM_IN, "Zoom In", "Увеличить", "확대") \
X(SHORTCUT_STRING_ZOOM_OUT, "Zoom Out", "Уменьшить", "축소") \
X(SNAPSHOT_RENAME_ANIMATION, "Rename Animation", "Переименовать анимацию", "애니메이션 이름 바꾸기") \
X(TEXT_RECORDING_PROGRESS, "Once recording is complete, rendering may take some time.\nPlease be patient...", "Когда запись завершена, рендеринг может занять некоторое время.\nПожалуйста потерпите...", "녹화가 완료되면 렌더링에 시간이 걸릴 수 있습니다.\n잠시만 기다려 주세요...") \
X(TEXT_OPEN_DIRECTORY, "Double-click to open directory in file explorer.", "Дважды нажмите, чтобы открыть директорию в проводнике.", "더블 클릭하여 파일 탐색기로 디렉터리를 엽니다.") \
X(TOAST_AUTOSAVE_FAILED, "Could not autosave document to: {0} ({1})", "Не получилось автосохранить документ в: {0} ({1})", "{0}에 파일을 자동저장할 수 없습니다. ({1})") \
X(TOAST_AUTOSAVING, "Autosaving...", "Автосохранение...", "자동저장 중...") \
X(TOAST_DESERIALIZE_ANIMATIONS_FAILED, "Failed to deserialize animation(s): {0}", "Не удалось десериализировать анимации: {0}", "애니메이션 역직렬화 실패: {0}") \
X(TOAST_DESERIALIZE_EVENTS_FAILED, "Failed to deserialize event(s): {0}", "Не удалось десериализировать события: {0}", "이벤트 역직렬화 실패: {0}") \
X(TOAST_DESERIALIZE_FRAMES_FAILED, "Failed to deserialize frames:", "Не удалось десериализировать кадры: {0}", "프레임 역직렬화 실패: {0}") \
X(TOAST_DESERIALIZE_FRAMES_NO_SELECTION, "Failed to deserialize frames: no selection.", "Не удалось десериализировать кадры: ничего не выбрано.", "프레임 역직렬화 실패. 선택한 것이 없습니다.") \
X(TOAST_DESERIALIZE_LAYERS_FAILED, "Failed to deserialize layer(s):", "Не удалось десериализировать слои: {0}", "레이어 역직렬화 실패: {0}") \
X(TOAST_DESERIALIZE_NULLS_FAILED, "Failed to deserialize null(s): {0}", "Не удалось десериализировать нули: {0}", "Null 역직렬화 실패: {0}") \
X(TOAST_DESERIALIZE_SPRITESHEETS_FAILED, "Failed to deserialize spritesheet(s): {0}", "Не удалось десериализировать спрайт-листы: {0}", "스프라이트 시트 역직렬화 실패: {0}") \
X(TOAST_EXPORT_RENDERED_ANIMATION, "Exported rendered animation to: {0}", "Рендерированные анимации экспортированы в: {0}", "{0}에 렌더링 된 애니메이션을 내보내기 했습니다.") \
X(TOAST_EXPORT_RENDERED_ANIMATION_FAILED, "Could not output rendered animation: {0}", "Не удалось вывести рендерированную анимацию: {0}", "{0}에 렌더링 된 애니메이션을 내보내기 할 수 없습니다.") \
X(TOAST_EXPORT_RENDERED_FRAMES, "Exported rendered frames to: {0}", "Рендерированные кадры экспортированы в: {0}", "{0}에 렌더링 된 프레임을 내보내기 했습니다.") \
X(TOAST_EXPORT_RENDERED_FRAMES_FAILED, "Could not export frames to: {0}", "Не удалось экспортировать кадры в: {0}", "프레임을 내보내기 할 수 없습니다. {0}") \
X(TOAST_EXPORT_SPRITESHEET, "Exported spritesheet to: {0}", "Спрайт-лист экспортирован в: {0}", "{0}에 스프라이트 시트를 내보내기 했습니다.") \
X(TOAST_EXPORT_SPRITESHEET_FAILED, "Could not export spritesheet to: {0}", "Не удалось экспортировать спрайт-лист в: {0}", "{0}에 스프라이트 시트를 내보내기 할 수 없습니다.") \
X(TOAST_ADD_SPRITESHEET_FAILED, "Failed to add spritesheet! Open a document first.", "Не удалось добавить спрайт-лист! Сначала откройте документ.", "스프라이트 시트를 추가할 수 없습니다! 먼저 문서를 여세요.") \
X(TOAST_ADD_SOUND_FAILED, "Failed to add sound! Open a document first.", "Не удалось добавить звук! Сначала откройте документ.", "사운드를 추가할 수 없습니다! 먼저 문서를 여세요.") \
X(TOAST_INVALID_FFMPEG_PATH, "FFmpeg path must point to a valid executable file.", "Путь к FFmpeg должен указывать к валидному файлу .exe.", "FFmpeg 경로는 유효한 실행 파일을 지정해야 합니다.") \
X(TOAST_NOT_SUPPORTED, "Operation not supported.", "Операция не поддерживается.", "해당 작업은 지원되지 않습니다.") \
X(TOAST_OPEN_DOCUMENT, "Opened document: {0}", "Открыт документ: {0}", "{0} 파일 열기") \
X(TOAST_OPEN_DOCUMENT_FAILED, "Failed to open document: {0} ({1})", "Не удалось открыть документ: {0} ({1})", "{0} 파일을 여는데 실패했습니다. {1}") \
X(TOAST_PNG_DIRECTORY_ACCESS_ERROR, "Could not access directory: {0} ({1})", "Не удалось получить доступ к директории: {0} ({1})", "{0} 디렉터리에 접근할 수 없습니다. {1}") \
X(TOAST_PNG_DIRECTORY_CREATE_ERROR, "Could not create directory: {0} ({1})", "Не удалось создать директорию: {0} ({1})", "{0} 디렉터리를 생성할 수 없습니다. {1}") \
X(TOAST_PNG_DIRECTORY_NOT_DIRECTORY, "PNG output path must be a directory: {0}", "Выходной путь для PNG должен быть директорией: {0}", "PNG를 출력할 경로는 디렉터리여야 합니다. {0}") \
X(TOAST_PNG_DIRECTORY_NOT_SET, "PNG output directory must be set.", "Директория для вывода PNG должна быть установлена.", "PNG를 출력할 경로를 설정해야 합니다.") \
X(TOAST_REDO, "Redo: {0}", "Повтор: {0}", "다시 실행: {0}") \
X(TOAST_RELOAD_SPRITESHEET, "Reloaded spritesheet #{0}: {1}", "Спрайт-лист #{0} перезагружен: {1}", "{0}번 스프라이트 시트 다시 불러옴: {1}") \
X(TOAST_REMOVE_SPRITESHEET, "Removed spritesheet #{0}: {1}", "Спрайт-лист #{0} удален: {1}", "{0}번 스프라이트 시트 제거됨: {1}") \
X(TOAST_REPLACE_SPRITESHEET, "Replaced spritesheet #{0}: {1}", "Спрайт-лист #{0} заменен: {1}", "{0}번 스프라이트 시트 교체됨: {1}") \
X(TOAST_SAVE_DOCUMENT, "Saved document to: {0}", "Документ сохранен в: {0}", "{0}에 파일을 저장했습니다.") \
X(TOAST_SAVE_DOCUMENT_FAILED, "Could not save document to: {0} ({1})", "Не удалось сохранить документ в: {0} ({1})", "{0}에 파일을 저장할 수 없습니다.") \
X(TOAST_SAVE_SPRITESHEET, "Saved spritesheet #{0}: {1}", "Спрайт-лист #{0} сохранен: {1}", "{1}에 {0}번 스프라이트 시트를 저장했습니다.") \
X(TOAST_SAVE_SPRITESHEET_FAILED, "Unable to save spritesheet #{0}: {1}", "Не удалось сохранить спрайт-лист #{0}: {1}", "{0}번 스프라이트 시트를 저장할 수 없습니다. {1}") \
X(TOAST_SOUNDS_DESERIALIZE_ERROR, "Failed to deserialize sound(s): {0}", "Не удалось десериализировать звуки: {0}", "사운드 역직렬화 실패: {0}") \
X(TOAST_SOUNDS_PASTE, "Paste Sound(s)", "Вставить звуки", "사운드 붙여넣기") \
X(TOAST_SOUND_INITIALIZED, "Initialized sound #{0}: {1}", "Звук #{0} инициализирован: {1}", "{0}번 사운드 초기화됨: {1}") \
X(TOAST_SOUND_INITIALIZE_FAILED, "Failed to initialize sound: {0}", "Не удалось инициализировать звуки: {0}", "사운드 초기화 실패: {0}") \
X(TOAST_SPRITESHEET_EMPTY, "Spritesheet export failed: captured frames are empty.", "Не удалось экспортировать спрайт-лист: захваченные кадры пусты.", "스프라이트 내보내기 실패: 캡처된 프레임이 비어있습니다.") \
X(TOAST_SPRITESHEET_INITIALIZED, "Initialized spritesheet #{0}: {1}", "Спрайт-лист #{0} инициализирован: {1}", "{0}번 스프라이트 시트 초기화됨: {1}") \
X(TOAST_SPRITESHEET_INIT_FAILED, "Failed to initialize spritesheet: {0}", "Не удалось инициализировать спрайт-лист: {0}", "스프라이트 시트 초기화 실패: {0}") \
X(TOAST_SPRITESHEET_NO_FRAMES, "No frames captured for spritesheet export.", "Ни один кадр не захвачен для экспорта спрайт-листа.", "스프라이트 시트 내보내기용으로 캡처된 프레임이 없습니다.") \
X(TOAST_UNDO, "Undo: {0}", "Отмена: {0}", "실행 취소: {0}") \
X(TOOLTIP_ADD_ANIMATION, "Add a new animation.", "Добавить новую анимацию.", "새 애니메이션을 추가합니다.") \
X(TOOLTIP_ADD_EVENT, "Add an event.", "Добавить событие.", "이벤트를 추가합니다.") \
X(TOOLTIP_ADD_ITEM, "Add the item, with the settings specified.", "Добавить предмет с указанными настройками.", "지정된 설정으로 항목을 추가합니다.") \
X(TOOLTIP_ADD_LAYER, "Add a layer.", "Добавить слой.", "레이어를 추가합니다.") \
X(TOOLTIP_ADD_NULL, "Add a null.", "Добавить нуль.", "Null을 추가합니다.") \
X(TOOLTIP_ADD_SPRITESHEET, "Add a new spritesheet.", "Добавить новый спрайт-лист.", "새 스프라이트 시트를 추가합니다.") \
X(TOOLTIP_ADD_VALUES, "Add the specified values onto each frame.\n(Boolean values will simply be set.)", "Добавить указанные значения к каждому кадру.\n(Булевы значения будут просто установлены.)", "각 프레임의 속성에 지정한 값을 더합니다.\n(참/거짓 값은 그대로 설정됩니다.)") \
X(TOOLTIP_ADJUST, "Set the value of each specified value onto the frame's equivalent.", "Установить значение каждого указанного значения к эквиваленту кадра.", "지정된 각 값을 프레임의 대응 값으로 설정합니다.") \
X(TOOLTIP_ALL_ITEMS_VISIBLE, "All items are visible. Press to only show layers.", "Все предметы видимы. Нажмите, чтобы только показать слои.", "모든 항목이 표시됩니다. 레이어만 표시하려면 누르세요.") \
X(TOOLTIP_ALT_ICONS, "Toggle a different appearance of the target icons.", "Переключить альтернативный вид иконок-перекрестий.", "대상 아이콘을 다른 외형으로 전환합니다.") \
X(TOOLTIP_ANIMATION_LENGTH, "Set the animation's length.", "Установить продолжительность анимации.", "애니메이션의 길이를 설정합니다.") \
X(TOOLTIP_AUTHOR, "Set the author of the document.", "Установить автора документа.", "파일의 작성자를 설정합니다.") \
X(TOOLTIP_AUTOSAVE_ENABLED, "Enables autosaving of documents.\\n(Does not overwrite files; makes copies to restore later.)", "Включает автосохранение документов.\n(Не заменяет файлы; создает копии для последующего восстановления.)", "파일 자동저장을 활성화합니다.\n(파일을 덮어쓰지 않습니다; 나중에 복원할 복사본을 만듭니다.)") \
X(TOOLTIP_AUTOSAVE_INTERVAL, "If changed, will autosave documents using this interval.", "Если изменено, будет автосохранять документы используя этот промежуток.", "이 간격으로 파일을 자동저장합니다.") \
X(TOOLTIP_AXES, "Toggle the axes' visibility.", "Переключить видимость осей.", "가로/세로 축을 표시하거나 숨깁니다.") \
X(TOOLTIP_AXES_COLOR, "Set the color of the axes.", "Установить цвет осей.", "가로/세로 축의 색상을 설정합니다.") \
X(TOOLTIP_BACKGROUND_COLOR, "Change the background color.", "Изменить цвет фона.", "배경색을 변경합니다.") \
X(TOOLTIP_BAKE_FRAMES, "Turn interpolated frames into uninterpolated ones.", "Превратить интерполированные кадры в неинтерполированных.", "연결된 프레임을 연결되지 않은 프레임으로 고정화합니다.") \
X(TOOLTIP_BAKE_FRAMES_OPTIONS, "Bake the selected frame(s) with the options selected.", "Запечь выбранные кадры с выбранными настройками.", "선택된 프레임을 선택한 옵션으로 베이킹합니다.") \
X(TOOLTIP_BORDER, "Toggle the visibility of borders around layers.", "Переключить видимость границ около слоев.", "레이어 주변 경계선을 표시하거나 숨깁니다.") \
X(TOOLTIP_CANCEL_ADD_ITEM, "Cancel adding an item.", "Отменить добавление предмета.", "항목 추가를 취소합니다.") \
X(TOOLTIP_CANCEL_BAKE_FRAMES, "Cancel baking frames.", "Отменить запечку кадров.", "프레임 베이킹을 취소합니다.") \
X(TOOLTIP_CENTER_VIEW, "Centers the view.", "Центрирует вид.", "미리보기 화면을 가운데에 맞춥니다.") \
X(TOOLTIP_CLOSE_SETTINGS, "Close without updating settings.", "Закрыть без обновления настройки.", "설정을 갱신하지 않고 닫습니다.") \
X(TOOLTIP_COLOR_OFFSET, "Change the color added onto the frame.", "Изменить цвет, который добавлен на кадр.", "프레임에 더해지는 색을 변경합니다.") \
X(TOOLTIP_COLUMNS, "Set how many columns the spritesheet will have.", "Установить сколько колонн будет иметь спрайт-лист.", "스프라이트 시트의 열 수를 설정합니다.") \
X(TOOLTIP_CROP, "Change the crop position the frame uses.", "Изменить позицию обрезки, которую использует кадр.", "프레임에 대응되는 스프라이트 시트를 어느 지점부터 사용할지 변경합니다.") \
X(TOOLTIP_CUSTOM_RANGE, "Toggle using a custom range for the animation.", "Переключить использование пользовательского диапазона для анимации.", "애니메이션에 사용자 지정 길이를 사용할지 정합니다.") \
X(TOOLTIP_DELETE_ANIMATIONS_AFTER, "Delete animations after merging them.", "Удалить анимации после их соединения.", "병합 후 기존 애니메이션을 삭제합니다.") \
X(TOOLTIP_DELETE_FRAMES, "Delete the selected frames.", "Удалить выбранные кадры.", "선택된 프레임을 삭제합니다.") \
X(TOOLTIP_DIVIDE_VALUES, "Divide the specified values for each frame.\\n(Boolean values will simply be set.)", "Разделить указанные значения для каждого кадра.\n(Булевы значения будут просто установлены.)", "각 프레임의 속성을 지정된 값으로 나눕니다.\n(참/거짓 값은 그대로 설정됩니다.)") \
X(TOOLTIP_DUPLICATE_ANIMATION, "Duplicate the selected animation(s).", "Дублировать выбранные анимации.", "선택된 애니메이션을 복제합니다.") \
X(TOOLTIP_DURATION, "Change how long the frame lasts.", "Изменить сколько длится кадр.", "프레임의 지속 시간을 변경합니다.") \
X(TOOLTIP_EDITOR_ZOOM, "Change the zoom of the editor.", "Изменить масштаб редактора.", "편집기의 줌을 변경합니다.") \
X(TOOLTIP_END, "Set the ending time of the animation.", "Установить конечное время анимации.", "애니메이션의 종료 시간을 설정합니다.") \
X(TOOLTIP_FFMPEG_PATH, "Set the path where the FFmpeg installation is located.\nFFmpeg is required to render animations.\nhttps://ffmpeg.org", "Установить путь, где находится установка FFmpeg.\nFFmpeg нужен для рендеринга анимаций.\nhttps://ffmpeg.org", "FFmpeg 설치 경로를 설정합니다.\n애니메이션 렌더링에는 FFmpeg가 필요합니다.\nhttps://ffmpeg.org") \
X(TOOLTIP_FIT, "Set the view to match the extent of the animation.", "Установить отображение чтобы оно соответствовало длительности анимации.", "미리보기 화면을 애니메이션 영역에 맞춥니다.") \
X(TOOLTIP_FIT_ANIMATION_LENGTH, "The animation length will be set to the effective length of the animation.", "Длительность анимации будет установлена в соответствии с фактической длиной анимации.", "애니메이션 길이가 애니메이션의 유효 길이로 설정됩니다.") \
X(TOOLTIP_FLIP_X, "Flip the X scale of the frame, to cheat mirroring the frame horizontally.\n(Note: the format does not support mirroring.)", "Отразить масштаб кадра по оси X, вместо отражения кадра горизонтально.\n(Примечание: формат не поддерживает нормальное отражение.)", "프레임의 가로 비율을 반전시켜 프레임이 수평으로 뒤집은 것처럼 보이게 합니다.\n(참고: 완전한 뒤집기 기능을 지원하지 않습니다.)") \
X(TOOLTIP_FLIP_Y, "Flip the Y scale of the frame, to cheat mirroring the frame vertically.\n(Note: the format does not support mirroring.)", "Отразить масштаб кадра по оси Y, вместо отражения кадра вертикально.\n(Примечание: формат не поддерживает нормальное отражение.)", "프레임의 세로 비율을 반전시켜 프레임이 수직으로 뒤집은 것처럼 보이게 합니다.\n(참고: 완전한 뒤집기 기능을 지원하지 않습니다.)") \
X(TOOLTIP_FORMAT, "For outputted images, each image will use this format.\\n{0} represents the index of each image.", "Для выведенных изображений, каждое будет использовать этот формат.\n{} представляет индекс каждого изображения.", "출력되는 이미지들은 이 형식을 사용합니다.\n{}는 각 이미지의 인덱스를 나타냅니다.") \
X(TOOLTIP_FPS, "Set the FPS of all animations.", "Установить сколько кадров в секунде для всех анимаций.", "모든 애니메이션의 FPS를 설정합니다.") \
X(TOOLTIP_FRAME_INTERPOLATION, "Toggle the frame interpolating; i.e., blending its values into the next frame based on the time.", "Переключить интерполяцию кадра; т. е. смешать его значения в следующий кадр на основе времени.", "프레임 보간(시간이 따라 속성 값이 다음 프레임의 값으로 변함) 여부를 정합니다.") \
X(TOOLTIP_FRAME_VISIBILITY, "Toggle the frame's visibility.", "Переключить видимость кадра.", "프레임을 표시하거나 숨깁니다.") \
X(TOOLTIP_GLOBAL_LOCALE, "Use the document's global locale.", "Использовать глобальный локаль документа.", "파일의 전역 경로를 사용합니다.") \
X(TOOLTIP_GRID_COLOR, "Change the grid's color.", "Изменить цвет сетки.", "격자 색을 변경합니다.") \
X(TOOLTIP_GRID_OFFSET, "Change the offset of the grid.", "Изменить смещение сетки.", "격자의 오프셋을 변경합니다.") \
X(TOOLTIP_GRID_SIZE, "Change the size of all cells in the grid.", "Изменить размер всех клеток в сетке.", "격자의 간격을 변경합니다.") \
X(TOOLTIP_GRID_SNAP, "Cropping will snap points to the grid.", "Обрезание привяжет точки к сетке.", "자르는 중점이 격자에 맞게 정렬됩니다.") \
X(TOOLTIP_GRID_VISIBILITY, "Toggle the visibility of the grid.", "Переключить видимость сетки.", "그리드의 표시 여부를 전환합니다.") \
X(TOOLTIP_INSERT_FRAME, "Insert a frame, based on the current selection.", "Вставить кадр на основе текущего выбора.", "현재 선택을 기반으로 프레임을 삽입합니다.") \
X(TOOLTIP_INTERVAL, "Set the maximum duration of each frame that will be baked.", "Установить максимальную длительносто каждого кадра, который будет запечен.", "베이크될 각 프레임의 최대 지속 시간을 설정합니다.") \
X(TOOLTIP_ITEM_NAME, "Set the item's name.", "Назвать предмет.", "항목의 이름을 설정합니다.") \
X(TOOLTIP_ITEM_VISIBILITY_HIDDEN, "The item is hidden. Press to show it.", "Этот предмет скрыт. Нажмите, чтобы его показать.", "항목이 숨겨져 있습니다. 표시하려면 누르세요.") \
X(TOOLTIP_ITEM_VISIBILITY_SHOWN, "The item is visible. Press to hide it.", "Этот предмет видим. Нажмите, чтобы его скрыть.", "항목이 표시되어 있습니다. 숨기려면 누르세요.") \
X(TOOLTIP_LANGUAGE, "Change the language of the program.", "Изменить язык программы.", "프로그램의 언어를 변경합니다.") \
X(TOOLTIP_LAYER_SPRITESHEET, "Set the layer item's spritesheet.", "Установить спрайт-лист слоевого предмета.", "레이어 항목의 스프라이트 시트를 설정합니다.") \
X(TOOLTIP_LAYER_TYPE, "Add a layer item.", "Использовать размер обрезки, который использует кадр.", "레이어 항목을 추가합니다.") \
X(TOOLTIP_LOCAL_LOCALE, "Use a local locale for the item.", "Изпользовать локальный локаль для предмета.", "항목에 지역 경로를 사용합니다.") \
X(TOOLTIP_LOOP_ANIMATION, "Toggle the animation looping.", "Переключить цикличное возпроизведение анимации.", "애니메이션을 반복할지 정합니다.") \
X(TOOLTIP_MOVE_TOOL_SNAP, "In Animation Preview, the Move tool will snap the frame's position right to the cursor, instead of being moved at a distance.", "В предпросмотре анимации, инструмент передвижения привяжет позицию кадра прямо к курсору, а не перемещает его на расстоянии.", "애니메이션 미리보기에서 이동 도구로 프레임을 이동시킬 때 프레임을 마우스 커서 바로 옆에 정렬되게 합니다.") \
X(TOOLTIP_MULTIPLY_VALUES, "Multiply the specified values for each frame.\\n(Boolean values will simply be set.)", "Умножить указанные значения для каждого кадра.\n(Булевы значения будут просто установлены.)", "각 프레임의 속성에 지정한 값을 곱합니다.\n(참/거짓 값은 그대로 설정됩니다.)") \
X(TOOLTIP_NEW_ITEM, "Create a new item.", "Создать новый предмет.", "새 항목을 만듭니다.") \
X(TOOLTIP_NO_UNUSED_ITEMS, "There are no unused items to use.", "Нет неиспользуемых предметов, которые использовать.", "사용하지 않는 항목이 없습니다.") \
X(TOOLTIP_NULL_NAME, "Set the null's name.", "Назвать этот нуль.", "Null의 이름을 설정합니다.") \
X(TOOLTIP_NULL_RECT, "The null will have a rectangle show around it.", "Около этого нуля будет прямоугольник.", "Null의 주변에 사각형이 표시됩니다.") \
X(TOOLTIP_NULL_RECT_HIDDEN, "The null's rectangle is hidden. Press to show it.", "Прямоугольник этого нуля скрыт. Нажмите, чтобы его показать.", "Null의 사각형이 숨겨져 있습니다. 표시하려면 누르세요.") \
X(TOOLTIP_NULL_RECT_SHOWN, "The null's rectangle is shown. Press to hide it.", "Прямоугольник этого нуля видим. Нажмите, чтобы его скрыть.", "Null의 사각형이 표시되어 있습니다. 숨기려면 누르세요.") \
X(TOOLTIP_NULL_TYPE, "Add a null item.", "Добавить нулевой предмет.", "Null 항목을 추가합니다.") \
X(TOOLTIP_ONIONSKIN_COLOR, "Change the color this onionskin will use.", "Изменить цвет, который использует оньонскин.", "전후 비교에서 사용할 색을 변경합니다.") \
X(TOOLTIP_ONIONSKIN_ENABLED, "Toggle onionskinning.", "Переключить оньонскин.", "전후 비교로 전환합니다.") \
X(TOOLTIP_ONIONSKIN_FRAMES, "Change the amount of frames this onionskin will use.", "Изменить кол-во кадров, которое будет использовать этот оньонскин.", "전후 비교에서 사용할 프레임 수를 변경합니다.") \
X(TOOLTIP_ONIONSKIN_INDEX, "The onionskinned frames will be based on frame index.", "Кадры оньонскина будут основаны на индексе кадров.", "프레임 비교를 프레임 인덱스를 기준으로 합니다.") \
X(TOOLTIP_ONIONSKIN_TIME, "The onionskinned frames will be based on frame time.", "Кадры оньонскина будут основаны на времени кадров.", "프레임 비교를 프레임 시간을 기준으로 합니다.") \
X(TOOLTIP_ONLY_LAYERS_VISIBLE, "Only layers are visible. Press to show all items.", "Только слои видимы. Нажмите, чтобы показать все предметы.", "레이어만 표시합니다. 모두 보려면 누르세요.") \
X(TOOLTIP_OPEN_MERGE_POPUP, "Open merge popup.", "Открыть всплывающее окно соединения.", "병합 팝업을 엽습니다.") \
X(TOOLTIP_OUTPUT_PATH, "Set the output path or directory for the animation.", "Установить путь или директорию вывода для анимации.", "애니메이션의 출력 경로 또는 디렉터리를 설정합니다.") \
X(TOOLTIP_OVERLAY, "Set an animation to be drawn over the current animation.", "Установить анимацию, которая будет выведена над текущей анимацией.", "현재 애니메이션 위에 그려질 애니메이션을 설정합니다.") \
X(TOOLTIP_OVERLAY_ALPHA, "Set the alpha of the overlayed animation.", "Установить прозрачность наложенной анимации.", "오버레이된 애니메이션의 불투명도를 설정합니다.") \
X(TOOLTIP_OVERWRITE_WARNING, "A warning will be shown when saving/overwriting a file.", "При сохранении/перезаписи файла будет показано предупреждение.", "저장하거나 덮어쓸 때 경고가 표시됩니다.") \
X(TOOLTIP_PAUSE_ANIMATION, "Pause the animation.", "Поставить анимацию на паузу.", "애니메이션을 일시정지합니다.") \
X(TOOLTIP_PIVOT, "Change the pivot of the frame; i.e., where it is centered.", "Изменить точку поворота кадра, т. е. где он центрирован.", "프레임의 중심점을 변경합니다.") \
X(TOOLTIP_PIVOTS, "Toggle the visibility of the animation's pivots.", "Переключить видимость точек поворота анимации.", "애니메이션의 중심점을 표시하거나 숨깁니다.") \
X(TOOLTIP_PLAYBACK_ALWAYS_LOOP, "Animations will always loop during playback, even if looping isn't set.", "Анимации всегда будут циклично воспроизводиться во время воспроизведения, даже если цикличность не установлена.", "반복 설정이 켜져 있지 않아도 애니메이션이 항상 반복 재생됩니다.") \
X(TOOLTIP_PLAYBACK_CLAMP, "Operations will always be clamped to within the animation's bounds.\nFor example, dragging the playhead, or triggers.", "Операции всегда будут ограничены границами анимации.\nНапример, перетаскивание воспроизводящей головки или триггеров.", "작업이 항상 애니메이션 범위 내로 제한됩니다.\n예: 플레이헤드를 드래그하거나 트리거를 조작할 때.") \
X(TOOLTIP_PLAY_ANIMATION, "Play the animation.", "Возпроизвести анимацию.", "애니메이션을 재생합니다.") \
X(TOOLTIP_POSITION, "Change the position of the frame.", "Изменить позицию кадра.", "프레임의 위치를 변경합니다.") \
X(TOOLTIP_PREVIEW_ZOOM, "Change the zoom of the preview.", "Изменить масштаб предпросмотра.", "미리보기의 줌을 변경합니다.") \
X(TOOLTIP_RAW, "Record only the raw animation; i.e., only its layers, to its bounds.", "Записывать только «сырую» анимацию, т. е. только ее слои.", "Raw 애니메이션만 녹화합니다. 즉, 레이어만 녹화합니다.") \
X(TOOLTIP_RELOAD_SPRITESHEETS, "Reloads the selected spritesheets.", "Перезагружает выбранные спрайт-листы.", "선택한 스프라이트 시트를 다시 불러옵니다.") \
X(TOOLTIP_REMOVE_ANIMATION, "Remove the selected animation(s).", "Удалить выбранные анимации.", "선택한 애니메이션을 제거합니다.") \
X(TOOLTIP_REMOVE_ITEMS, "Remove the selected item(s).", "Удалить выбранные предметы.", "선택한 항목을 제거합니다.") \
X(TOOLTIP_REMOVE_UNUSED_EVENTS, "Remove unused events (i.e., ones not used by any trigger in any animation.)", "Удалить неиспользуемые события (т. е. события, которые не использует ни один триггер в ни одной анимации.)", "사용되지 않는 이벤트(어떤 애니메이션의 트리거에서도 사용되지 않는 것)를 제거합니다.") \
X(TOOLTIP_REMOVE_UNUSED_LAYERS, "Remove unused layers (i.e., ones not used in any animation.)", "Удалить неиспользуемые слои (т. е. слои, которые не используются ни одной анимацией.)", "사용되지 않는 레이어(어떤 애니메이션에서도 사용되지 않는 것)를 제거합니다.") \
X(TOOLTIP_REMOVE_UNUSED_NULLS, "Remove unused nulls (i.e., ones not used in any animation.)", "Удалить неиспользуемые нули (т. е. нули, которые не используются ни одной анимацией.)", "사용되지 않는 Null(어떤 애니메이션에서도 사용되지 않는 것)을 제거합니다.") \
X(TOOLTIP_REMOVE_UNUSED_SOUNDS, "Remove unused sounds (i.e., ones not used in any trigger.)", "Удалить неиспользуемые звуки (т. е. звуки, которые не используются ни одным триггером.)", "사용되지 않는 사운드(어떤 트리거에서도 사용되지 않는 것)를 제거합니다.") \
X(TOOLTIP_REMOVE_UNUSED_SPRITESHEETS, "Remove all unused spritesheets (i.e., not used in any layer.).", "Удалить неиспользуемые спрайт-листы (т. е. те, которые не используются ни одним слоем.)", "사용되지 않는 모든 스프라이트 시트(어떤 레이어에서도 사용되지 않는 것)를 제거합니다.") \
X(TOOLTIP_RENDER_BUTTON, "Render the animation using the current settings.", "Рендерировать анимацию, используя текущие настройки.", "현재 설정으로 애니메이션을 렌더링합니다.") \
X(TOOLTIP_RENDER_TYPE, "Set the type of the output.", "Установить тип вывода.", "출력 유형을 설정합니다.") \
X(TOOLTIP_REPEAT_DELAY, "Set how often, after repeating begins, key inputs will be fired.", "Установить, как часто после начала повторения будут срабатывать нажатия клавиш.", "반복 입력이 시작된 후 입력이 얼마나 자주 들어갈지 설정합니다.") \
X(TOOLTIP_REPLACE_SPRITESHEET, "Replace the selected spritesheet with a new one.", "Заменить выбранный спрайт-лист на новый.", "선택된 스프라이트 시트를 새 시트로 교체합니다.") \
X(TOOLTIP_ROOT_TRANSFORM, "Root frames will transform the rest of the animation.", "Корневые кадры трансформируют остаток анимации.", "Root 프레임이 나머지 애니메이션을 변형합니다.") \
X(TOOLTIP_ROTATION, "Change the rotation of the frame.", "Изменить поворот кадра.", "프레임의 회전값을 변경합니다.") \
X(TOOLTIP_ROUND_ROTATION, "Rotation will be rounded to the nearest whole number.", "Поворот будет округлен к самому близкому целому числу.", "회전값을 가장 가까운 정수로 반올림합니다.") \
X(TOOLTIP_ROUND_SCALE, "Scale will be rounded to the nearest whole number.", "Масштаб будет округлен к самому близкому целому числу.", "비율을 가장 가까운 정수로 반올림합니다.") \
X(TOOLTIP_ROWS, "Set how many rows the spritesheet will have.", "Установить сколько рядов будет иметь спрайт-лист.", "스프라이트 시트의 행 수를 설정합니다.") \
X(TOOLTIP_SAVE_SPRITESHEETS, "Save the selected spritesheets.", "Сохранить выбранные спрайт-листы.", "선택한 스프라이트 시트를 저장합니다.") \
X(TOOLTIP_SCALE, "Change the scale of the frame, in percent.", "Изменить масштаб кадра.", "프레임의 비율을 변경합니다.") \
X(TOOLTIP_SCALE_OUTPUT, "Set the output scale of the animation.", "Установить масштаб анимации при выводе.", "애니메이션의 출력 비율을 설정합니다.") \
X(TOOLTIP_SETTINGS_SAVE, "Use the configured settings.", "Использовать настроенные настройки.", "구성된 설정을 사용합니다.") \
X(TOOLTIP_SET_DEFAULT_ANIMATION, "Set the selected animation as the default.", "Установить выбранную анимацию как анимацию по умолчанию.", "선택한 애니메이션을 기본 애니메이션으로 설정합니다.") \
X(TOOLTIP_SET_TO_RECOMMENDED, "Use a recommended value for rows/columns.", "Использовать рекомендованное значение для рядов/колонн.", "행/열에 권장값을 사용합니다.") \
X(TOOLTIP_SIZE, "Change the crop size the frame uses.", "Изменить размер обрезки, который использует этот кадр.", "프레임에 대응되는 스프라이트 시트의 사용 영역의 크기를 변경합니다.") \
X(TOOLTIP_SOUND, "Toggle sounds playing with triggers.\nBind sounds to events in the Events window.", "Переключить воспроизведения звуков с помощью триггеров.\nПривязывайте звуки к событиям в окне событий.", "트리거와 함께 사운드를 재생할지 정합니다.\n사운드는 이벤트 창에서 이벤트에 연결하세요.") \
X(TOOLTIP_SOUNDS_PLAY, "Click to play.", "Нажмите, чтобы возпроизвести.", "클릭하여 재생합니다.") \
X(TOOLTIP_SOUND_INVALID, "This sound could not be loaded. Replace the file.", "Этот звук не удалось загрузить. Замените файл.", "이 사운드를 불러올 수 없습니다. 파일을 교체하세요.") \
X(TOOLTIP_SOUND_ADD, "Add a sound.", "Добавить звук.", "사운드를 추가합니다.") \
X(TOOLTIP_SPRITESHEET_BORDER, "Toggle a border appearing around the spritesheet.", "Переключить показ границ около спрайт-листа.", "스프라이트 시트 주변에 경계선을 표시하거나 숨깁니다.") \
X(TOOLTIP_SPRITESHEET_INVALID, "This spritesheet isn't valid!\\nLoad an existing, valid texture.", "Этот спрайт-лист невалиден!\nЗагрузите существующую, валидную текстуру.", "이 스프라이트 시트는 유효하지 않습니다!\n유효한 텍스처를 불러오세요.") \
X(TOOLTIP_STACK_SIZE, "Set the maximum snapshot stack size of a document (i.e., how many undo/redos are preserved at a time).", "Установить максимальный размер стека снимков документа (т. е. количество отмен/повторов, сохраняемых одновременно).", "파일의 최대 스냅숏 스택 크기(즉 한 번에 보존되는 실행 취소/다시 실행 수)를 설정합니다.") \
X(TOOLTIP_START, "Set the starting time of the animation.", "Установить начальное время анимации.", "애니메이션의 시작 시간을 설정합니다.") \
X(TOOLTIP_SUBTRACT_VALUES, "Subtract the specified values from each frame.\\n(Boolean values will simply be set.)", "Вычтить указанные значения из каждого кадра.\n(Булевы значения будут просто указаны.)", "각 프레임의 속성에 지정한 값을 뺍니다.\n(참/거짓 값은 그냥 설정됩니다.)") \
X(TOOLTIP_TIMELINE_SHORTCUTS, "- Press {0} to decrement time.\n- Press {1} to increment time.\n- Press {2} to shorten the selected frame, by one frame.\n- Press {3} to extend the selected frame, by one frame.\n- Press {4} to go to the previous frame.\\n- Press {5} to go to the next frame.\n- Click and hold on a frame while holding CTRL to change its duration.\\n- Click and hold on a trigger to change its At Frame.\n- Hold Alt while clicking a non-trigger frame to toggle interpolation.", "- Нажмите {0}, чтобы уменьшить время.\n- Нажмите {1}, чтобы увеличить время.\n- Нажмите {2}, чтобы укоротить выбранный кадр одной мерной единицей.\n- Нажмите {3}, чтобы продлить выбранный кадр на одну мерную единицу.\n- Нажмите {4}, чтобы перейти к предыдущему кадру.\n- Нажмите {5}, чтобы перейти к следующему кадру.\n- Удерживайте нажатой кнопку мыши по кадру, удерживая CTRL, чтобы изменить его длительность.\n- Нажмите и удерживайте кнопку мыши по триггеру, чтобы изменить параметр «На кадре».\n- Удерживайте Alt и нажмите по кадру, который не является триггером, чтобы переключить интерполяцию.", "- {0} 키: 플레이헤드를 뒤로 보냅니다.\n- {1} 키: 플레이헤드를 앞으로 보냅니다.\n- {2} 키: 선택한 프레임을 한 프레임 단축합니다.\n- {3} 키: 선택한 프레임을 한 프레임 연장합니다.\n- {4} 키: 이전 프레임을 선택합니다.\n- {5} 키: 다음 프레임을 선택합니다.\n- CTRL 키를 누른 채 프레임을 클릭하고 드래그하면 프레임의 유지 시간을 변경할 수 있습니다.\n- 트리거를 클릭하고 드래그하면 트리거의 시작 프레임을 변경할 수 있습니다.\n- Alt 키를 누른 채 트리거를 제외한 프레임을 클릭하면 매끄럽게 연결 설정을 켤 수 있습니다.") \
X(TOOLTIP_TINT, "Change the tint of the frame.", "Изменить оттенок кадра.", "프레임의 색조를 변경합니다.") \
X(TOOLTIP_TOOL_COLOR, "Selects the color to be used for drawing.\n(Spritesheet Editor only.)", "Выбирает цвет, который будет использоваться для рисования.\n(Только в редакторе спрайт-листов.)", "그리기용으로 사용할 색을 선택합니다.\n(스프라이트 시트 편집기 전용)") \
X(TOOLTIP_TOOL_COLOR_PICKER, "Selects a color from the canvas.\n(Spritesheet Editor only.)", "Выбирает цвет из холста.\n(Только в редакторе спрайт-листов.)", "캔버스에서 색을 선택합니다.\n(스프라이트시트 편집기 전용)") \
X(TOOLTIP_TOOL_CROP, "Use the crop tool.\nWill produce a crop rectangle based on how the cursor is dragged, or directional keys are pressed.\nHold CTRL with arrow keys to change position.\nHolding right click will use the Move tool's functionality.\n(Spritesheet Editor only.)", "Использовать инструмент обрезки.\nБудет создан прямоугольник обрезки в зависимости от перемещения курсора или нажатия клавиш со стрелками.\nУдерживайте клавишу CTRL вместе с клавишами со стрелками, чтобы изменить положение.\nУдерживание правой кнопки мыши позволит использовать функциональность инструмента перемещения.\n(Только в редакторе спрайт-листов.)", "자르기 도구를 사용합니다.\n커서를 드래그하거나 방향키를 눌러 자르기 사각형을 만듭니다.\n화살표 키와 CTRL 키를 같이 눌러 위치를 변경합니다.\n오른쪽 클릭을 누른 채 사용하면 이동 도구의 기능이 대신 사용됩니다.\n(스프라이트 시트 편집기 전용)") \
X(TOOLTIP_TOOL_DRAW, "Draws pixels onto the selected spritesheet, with the current color.\n(Spritesheet Editor only.)", "Рисует пиксели на выбранный спрайт-лист текущим цветом.\n(Только в редакторе спрайт-листов.)", "선택된 스프라이트 시트에 픽셀을 그립니다. 현재 색을 사용합니다.\n(스프라이트 시트 편집기 전용)") \
X(TOOLTIP_TOOL_ERASE, "Erases pixels from the selected spritesheet.\n(Spritesheet Editor only.)", "Стирает пиксели из выбранного спрайт-листа.\n(Только в редакторе спрайт-листов.)", "선택된 스프라이트 시트에서 픽셀을 지웁니다.\n(스프라이트 시트 편집기 전용)") \
X(TOOLTIP_TOOL_MOVE, "Use the move tool.\nAnimation Preview: Will move the position of the frame.\nSpritesheet Editor: Will move the pivot, and holding right click will use the Crop functionality instead.\nUse mouse or directional keys to change the value.", "Изпользовать инструмент перемещения.\nВ предпросмотрe анимации: Переместит позицию кадра.\nВ редакторе спрайт-листов: Переместит точку поворота, а удерживая правой кнопкой мыши позволит использовать функциональность инструмента обрезки.\nИзпользуйте мышь или клавиши со стрелками, чтобы изменить значение.", "이동 도구를 사용합니다.\n애니메이션 미리보기: 프레임의 위치를 이동합니다.\n스프라이트 시트 편집기: 중심점을 이동하며, 오른쪽 클릭을 누른 채 사용하면 자르기 도구의 기능이 대신 사용됩니다.\n값을 변경하려면 마우스나 방향키를 사용하세요.") \
X(TOOLTIP_TOOL_PAN, "Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou can also use the middle mouse button to pan at any time.", "Использовать инструмент панорамирования. Будет смещать вид при перемещении курсора.\nВы также можете использовать среднюю кнопку мыши для панорамирования в любое время.", "시점 이동 도구를 사용합니다.\n커서를 드래그하면 시점이 이동합니다.\n언제든지 중간 마우스 버튼으로도 이동할 수 있습니다.") \
X(TOOLTIP_TOOL_REDO, "Redoes the last action.", "Повторяет последнее действие.", "마지막 작업을 다시 실행합니다.") \
X(TOOLTIP_TOOL_ROTATE, "Use the rotate tool.\nWill rotate the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)", "Использовать инструмент поворота.\nПовернет выбранный предмет при перемещении курсора или при нажатии клавишей со стрелками.\n(Только в предпросмотре анимации.)", "회전 도구를 사용합니다.\n커서를 드래그하거나 방향키를 눌러 선택한 항목을 회전합니다.\n(애니메이션 미리보기 전용)") \
X(TOOLTIP_TOOL_SCALE, "Use the scale tool.\nWill scale the selected item as the cursor is dragged, or directional keys are pressed.\nHold SHIFT to lock scaling to one dimension.\n(Animation Preview only.)", "Использовать инструмент масштабирования.\nВыбранный элемент будет масштабироваться при перетаскивании курсора или нажатии клавиш со стрелками.\nУдерживайте клавишу SHIFT, чтобы заблокировать масштабирование в одном измерении.\n(Только в предпросмотре анимации.)", "비율 도구를 사용합니다.\n커서를 드래그하거나 방향키를 눌러 선택한 항목의 비율을 조정합니다.\nSHIFT 키를 누르면 한 축으로 고정됩니다.\n(애니메이션 미리보기 전용)") \
X(TOOLTIP_TOOL_UNDO, "Undoes the last action.", "Отменяет последнее действие.", "마지막 작업을 취소합니다.") \
X(TOOLTIP_TO_ANIMATION_RANGE, "Set the range to the normal range of the animation.", "Установить диапазон равным обычному диапазону анимации.", "렌더링 범위를 애니메이션의 범위로 설정합니다.") \
X(TOOLTIP_TO_SELECTED_FRAMES, "If frames are selected, use that range for the rendered animation.", "Если кадры выбраны, использовать этот диапазон для рендерированной анимации.", "프레임이 선택된 경우 그 영역만 렌더링 범위로 사용합니다.") \
X(TOOLTIP_TRANSPARENT, "Toggle the spritesheet editor being transparent.", "Переключить прозрачность спрайт-листа.", "스프라이트 시트 편집기를 투명하게 전환합니다.") \
X(TOOLTIP_TRIGGER_AT_FRAME, "Change the frame the trigger will be activated at.", "Изменить кадр, на котором будет активироваться триггер.", "트리거가 활성화될 프레임을 변경합니다.") \
X(TOOLTIP_TRIGGER_EVENT, "Change the event this trigger uses.", "Изменить событие, которое использует этот триггер.", "이 트리거가 사용할 이벤트를 변경합니다.") \
X(TOOLTIP_TRIGGER_SOUND, "Change the sound this trigger uses.", "Изменить звук, который использует этот триггер.", "이 트리거가 사용할 사운드를 변경합니다.") \
X(TOOLTIP_TRIGGER_VISIBILITY, "Toggle the trigger's visibility.", "Переключить видимость триггера.", "트리거를 표시하거나 숨깁니다.") \
X(TOOLTIP_UI_SCALE, "Change the scale of the UI.", "Изменить масштаб пользовательского интерфейса.", "UI 비율을 변경합니다.") \
X(TOOLTIP_UNUSED_ITEMS_HIDDEN, "Unused layers/nulls are hidden. Press to show them.", "Неиспользуемые слои/нули скрыты. Нажмите, чтобы их показать.", "사용되지 않는 레이어/Null이 숨겨져 있습니다. 표시하려면 누르세요.") \
X(TOOLTIP_UNUSED_ITEMS_SHOWN, "Unused layers/nulls are shown. Press to hide them.", "Неиспользуемые слои/нули видимы. Нажмите, чтобы их скрыть.", "사용되지 않는 레이어/Null이 표시되어 있습니다. 숨기려면 누르세요.") \
X(TOOLTIP_USE_DEFAULT_SETTINGS, "Reset the settings to their defaults.", "Сбросить настройки на настройки по умолчанию.", "설정을 기본값으로 재설정합니다.") \
X(TOOLTIP_USE_EXISTING_ITEM, "Reuse an unused item instead of creating a new one.", "Использовать неиспользуемый предмет, а не создавать новый.", "새로 만들지 않고 사용되지 않는 항목을 재사용합니다.") \
X(TOOLTIP_VSYNC, "Toggle vertical sync; synchronizes program update rate with monitor refresh rate.", "Переключить вертикальную синхронизацию; синхронизирует частоту обновления программы с частотой обновления монитора.", "수직 동기화를 켜거나 끕니다. 프로그램의 업데이트 속도를 모니터의 새로고침 속도와 동기화합니다.") \
X(TOOLTIP_ZOOM_STEP, "When zooming in/out with mouse or shortcut, this value will be used.", "При масштабировании мышью или горячей клавишей будет использоваться это значение.", "마우스나 단축키로 확대/축소할 때 이 값이 사용됩니다.")
// clang-format on
enum StringType
{
@@ -259,22 +571,19 @@ namespace anm2ed
STRING_TYPE_COUNT
};
#define DEFINE_LANGUAGE_ARRAY(idx, symbol, label, selector) \
#define DEFINE_LANGUAGE_ARRAY(symbol, label, selector) \
static constexpr const char* STRINGS_##symbol[] = {STRINGS(selector)};
LANGUAGES(DEFINE_LANGUAGE_ARRAY)
#undef DEFINE_LANGUAGE_ARRAY
static constexpr const char* const* LANGUAGE_STRING_COLLECTIONS[LANGUAGE_COUNT] = {
#define X(idx, symbol, label, selector) STRINGS_##symbol,
#define X(symbol, label, selector) STRINGS_##symbol,
LANGUAGES(X)
#undef X
};
#undef SELECT_ENGLISH
#undef SELECT_SPANISH
#undef SELECT_RUSSIAN
#undef SELECT_TURKISH
#undef SELECT_CHINESE
#undef SELECT_KOREAN
#undef STRINGS
#undef LANGUAGES
@@ -284,10 +593,11 @@ namespace anm2ed
public:
Language language{ENGLISH};
inline const char* get(StringType type) const
inline const char* get(StringType type, Language setLanguage = (Language)-1) const
{
if (language < 0 || language >= LANGUAGE_COUNT || type < 0 || type >= STRING_TYPE_COUNT) return "undefined";
return LANGUAGE_STRING_COLLECTIONS[language][type];
auto useLanguage = (int)setLanguage == -1 ? language : setLanguage;
if (useLanguage < 0 || useLanguage >= LANGUAGE_COUNT || type < 0 || type >= STRING_TYPE_COUNT) return "undefined";
return LANGUAGE_STRING_COLLECTIONS[useLanguage][type];
}
};
}

View File

@@ -11,7 +11,10 @@ namespace anm2ed
Resources::Resources()
{
for (auto [i, fontInfo] : std::views::enumerate(font::FONTS))
{
fonts[i] = Font((void*)fontInfo.data, fontInfo.length, font::SIZE);
fonts[i].append((void*)font::CJK_INFO.data, font::CJK_INFO.length, font::SIZE);
}
for (auto [i, iconInfo] : std::views::enumerate(icon::ICONS))
icons[i] = Texture(iconInfo.data, iconInfo.length, iconInfo.size);

View File

@@ -6,6 +6,7 @@
#include "anm2/anm2_type.h"
#include "render.h"
#include "strings.h"
#include "types.h"
namespace anm2ed
@@ -41,182 +42,183 @@ namespace anm2ed
#define SETTINGS_MEMBERS \
/* Symbol / Name / String / Type / Default */ \
X(WINDOW_SIZE, windowSize, "Window Size", IVEC2_WH, {1600, 900}) \
X(IS_VSYNC, isVsync, "Vsync", BOOL, true) \
X(UI_SCALE, uiScale, "UI Scale", FLOAT, 1.0f) \
X(THEME, theme, "Theme", INT, types::theme::DARK) \
X(WINDOW_SIZE, windowSize, STRING_UNDEFINED, IVEC2_WH, {1600, 900}) \
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) \
X(LANGUAGE, language, STRING_UNDEFINED, INT, ENGLISH) \
\
X(FILE_IS_AUTOSAVE, fileIsAutosave, "Autosave", BOOL, true) \
X(FILE_AUTOSAVE_TIME, fileAutosaveTime, "Autosave Time", INT, 1) \
X(FILE_IS_WARN_OVERWRITE, fileIsWarnOverwrite, "Warn on Overwrite", BOOL, true) \
X(FILE_SNAPSHOT_STACK_SIZE, fileSnapshotStackSize, "Snapshot Stack Size", INT, 50) \
X(FILE_IS_AUTOSAVE, fileIsAutosave, STRING_UNDEFINED, BOOL, true) \
X(FILE_AUTOSAVE_TIME, fileAutosaveTime, STRING_UNDEFINED, INT, 1) \
X(FILE_IS_WARN_OVERWRITE, fileIsWarnOverwrite, STRING_UNDEFINED, BOOL, true) \
X(FILE_SNAPSHOT_STACK_SIZE, fileSnapshotStackSize, STRING_UNDEFINED, INT, 50) \
\
X(KEYBOARD_REPEAT_DELAY, keyboardRepeatDelay, "Repeat Delay", FLOAT, 0.300f) \
X(KEYBOARD_REPEAT_RATE, keyboardRepeatRate, "Repeat Rate", FLOAT, 0.050f) \
X(KEYBOARD_REPEAT_DELAY, keyboardRepeatDelay, STRING_UNDEFINED, FLOAT, 0.300f) \
X(KEYBOARD_REPEAT_RATE, keyboardRepeatRate, STRING_UNDEFINED, FLOAT, 0.050f) \
\
X(INPUT_ZOOM_STEP, inputZoomStep, "Zoom Step", FLOAT, 50.0f) \
X(INPUT_IS_MOVE_TOOL_SNAP_TO_MOUSE, inputIsMoveToolSnapToMouse, "Move Tool: Snap to Mouse", BOOL, false) \
X(INPUT_ZOOM_STEP, inputZoomStep, STRING_UNDEFINED, FLOAT, 50.0f) \
X(INPUT_IS_MOVE_TOOL_SNAP_TO_MOUSE, inputIsMoveToolSnapToMouse, STRING_UNDEFINED, BOOL, false) \
\
X(PLAYBACK_IS_LOOP, playbackIsLoop, "Loop", BOOL, true) \
X(PLAYBACK_IS_CLAMP, playbackIsClamp, "Clamp", BOOL, true) \
X(PLAYBACK_IS_LOOP, playbackIsLoop, STRING_UNDEFINED, BOOL, true) \
X(PLAYBACK_IS_CLAMP, playbackIsClamp, STRING_UNDEFINED, BOOL, true) \
\
X(CHANGE_IS_CROP, changeIsCrop, "##Is Crop", BOOL, false) \
X(CHANGE_IS_SIZE, changeIsSize, "##Is Size", BOOL, false) \
X(CHANGE_IS_POSITION, changeIsPosition, "##Is Position", BOOL, false) \
X(CHANGE_IS_PIVOT, changeIsPivot, "##Is Pivot", BOOL, false) \
X(CHANGE_IS_SCALE, changeIsScale, "##Is Scale", BOOL, false) \
X(CHANGE_IS_ROTATION, changeIsRotation, "##Is Rotation", BOOL, false) \
X(CHANGE_IS_DURATION, changeIsDuration, "##Is Duration", BOOL, false) \
X(CHANGE_IS_TINT, changeIsTint, "##Is Tint", BOOL, false) \
X(CHANGE_IS_COLOR_OFFSET, changeIsColorOffset, "##Is Color Offset", BOOL, false) \
X(CHANGE_IS_VISIBLE_SET, changeIsVisibleSet, "##Is Visible", BOOL, false) \
X(CHANGE_IS_INTERPOLATED_SET, changeIsInterpolatedSet, "##Is Interpolated", BOOL, false) \
X(CHANGE_CROP, changeCrop, "Crop", VEC2, {}) \
X(CHANGE_SIZE, changeSize, "Size", VEC2, {}) \
X(CHANGE_POSITION, changePosition, "Position", VEC2, {}) \
X(CHANGE_PIVOT, changePivot, "Pivot", VEC2, {}) \
X(CHANGE_SCALE, changeScale, "Scale", VEC2, {}) \
X(CHANGE_ROTATION, changeRotation, "Rotation", FLOAT, 0.0f) \
X(CHANGE_DURATION, changeDuration, "Duration", INT, 0) \
X(CHANGE_TINT, changeTint, "Tint", VEC4, {}) \
X(CHANGE_COLOR_OFFSET, changeColorOffset, "Color Offset", VEC3, {}) \
X(CHANGE_IS_VISIBLE, changeIsVisible, "Visible", BOOL, false) \
X(CHANGE_IS_INTERPOLATED, changeIsInterpolated, "Interpolated", BOOL, false) \
X(CHANGE_NUMBER_FRAMES, changeNumberFrames, "Frame Count", INT, 1) \
X(CHANGE_IS_FROM_SELECTED_FRAME, changeIsFromSelectedFrame, "From Selected Frame", BOOL, false) \
X(CHANGE_IS_CROP, changeIsCrop, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_SIZE, changeIsSize, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_POSITION, changeIsPosition, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_PIVOT, changeIsPivot, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_SCALE, changeIsScale, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_ROTATION, changeIsRotation, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_DURATION, changeIsDuration, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_TINT, changeIsTint, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_COLOR_OFFSET, changeIsColorOffset, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_VISIBLE_SET, changeIsVisibleSet, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_INTERPOLATED_SET, changeIsInterpolatedSet, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_CROP, changeCrop, STRING_UNDEFINED, VEC2, {}) \
X(CHANGE_SIZE, changeSize, STRING_UNDEFINED, VEC2, {}) \
X(CHANGE_POSITION, changePosition, STRING_UNDEFINED, VEC2, {}) \
X(CHANGE_PIVOT, changePivot, STRING_UNDEFINED, VEC2, {}) \
X(CHANGE_SCALE, changeScale, STRING_UNDEFINED, VEC2, {}) \
X(CHANGE_ROTATION, changeRotation, STRING_UNDEFINED, FLOAT, 0.0f) \
X(CHANGE_DURATION, changeDuration, STRING_UNDEFINED, INT, 0) \
X(CHANGE_TINT, changeTint, STRING_UNDEFINED, VEC4, {}) \
X(CHANGE_COLOR_OFFSET, changeColorOffset, STRING_UNDEFINED, VEC3, {}) \
X(CHANGE_IS_VISIBLE, changeIsVisible, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_IS_INTERPOLATED, changeIsInterpolated, STRING_UNDEFINED, BOOL, false) \
X(CHANGE_NUMBER_FRAMES, changeNumberFrames, STRING_UNDEFINED, INT, 1) \
X(CHANGE_IS_FROM_SELECTED_FRAME, changeIsFromSelectedFrame, STRING_UNDEFINED, BOOL, false) \
\
X(SCALE_VALUE, scaleValue, "Scale", FLOAT, 1.0f) \
X(SCALE_VALUE, scaleValue, STRING_UNDEFINED, FLOAT, 1.0f) \
\
X(PREVIEW_IS_AXES, previewIsAxes, "Axes", BOOL, true) \
X(PREVIEW_IS_GRID, previewIsGrid, "Grid", BOOL, true) \
X(PREVIEW_IS_ROOT_TRANSFORM, previewIsRootTransform, "Root Transform", BOOL, true) \
X(PREVIEW_IS_PIVOTS, previewIsPivots, "Pivots", BOOL, false) \
X(PREVIEW_IS_BORDER, previewIsBorder, "Border", BOOL, false) \
X(PREVIEW_IS_ALT_ICONS, previewIsAltIcons, "Alt Icons", BOOL, false) \
X(PREVIEW_OVERLAY_TRANSPARENCY, previewOverlayTransparency, "Alpha", FLOAT, 255) \
X(PREVIEW_START_ZOOM, previewStartZoom, "Start Zoom", FLOAT, 200.0f) \
X(PREVIEW_GRID_SIZE, previewGridSize, "Size", IVEC2, {32, 32}) \
X(PREVIEW_GRID_OFFSET, previewGridOffset, "Offset", IVEC2, {}) \
X(PREVIEW_GRID_COLOR, previewGridColor, "Color", VEC4, {1.0f, 1.0f, 1.0f, 0.125f}) \
X(PREVIEW_AXES_COLOR, previewAxesColor, "Color", VEC4, {1.0f, 1.0f, 1.0f, 0.125f}) \
X(PREVIEW_BACKGROUND_COLOR, previewBackgroundColor, "Background Color", VEC3, {0.113f, 0.184f, 0.286f}) \
X(PREVIEW_IS_AXES, previewIsAxes, STRING_UNDEFINED, BOOL, true) \
X(PREVIEW_IS_GRID, previewIsGrid, STRING_UNDEFINED, BOOL, true) \
X(PREVIEW_IS_ROOT_TRANSFORM, previewIsRootTransform, STRING_UNDEFINED, BOOL, true) \
X(PREVIEW_IS_PIVOTS, previewIsPivots, STRING_UNDEFINED, BOOL, false) \
X(PREVIEW_IS_BORDER, previewIsBorder, STRING_UNDEFINED, BOOL, false) \
X(PREVIEW_IS_ALT_ICONS, previewIsAltIcons, STRING_UNDEFINED, BOOL, false) \
X(PREVIEW_OVERLAY_TRANSPARENCY, previewOverlayTransparency, STRING_UNDEFINED, FLOAT, 255) \
X(PREVIEW_START_ZOOM, previewStartZoom, STRING_UNDEFINED, FLOAT, 200.0f) \
X(PREVIEW_GRID_SIZE, previewGridSize, STRING_UNDEFINED, IVEC2, {32, 32}) \
X(PREVIEW_GRID_OFFSET, previewGridOffset, STRING_UNDEFINED, IVEC2, {}) \
X(PREVIEW_GRID_COLOR, previewGridColor, STRING_UNDEFINED, VEC4, {1.0f, 1.0f, 1.0f, 0.125f}) \
X(PREVIEW_AXES_COLOR, previewAxesColor, STRING_UNDEFINED, VEC4, {1.0f, 1.0f, 1.0f, 0.125f}) \
X(PREVIEW_BACKGROUND_COLOR, previewBackgroundColor, STRING_UNDEFINED, VEC3, {0.113f, 0.184f, 0.286f}) \
\
X(PROPERTIES_IS_ROUND, propertiesIsRound, "Round", BOOL, false) \
X(PROPERTIES_IS_ROUND, propertiesIsRound, STRING_UNDEFINED, BOOL, false) \
\
X(GENERATE_START_POSITION, generateStartPosition, "Start Position", IVEC2, {}) \
X(GENERATE_SIZE, generateSize, "Size", IVEC2, {64, 64}) \
X(GENERATE_PIVOT, generatePivot, "Pivot", IVEC2, {32, 32}) \
X(GENERATE_ROWS, generateRows, "Rows", INT, 4) \
X(GENERATE_COLUMNS, generateColumns, "Columns", INT, 4) \
X(GENERATE_COUNT, generateCount, "Count", INT, 16) \
X(GENERATE_DURATION, generateDuration, "Duration", INT, 1) \
X(GENERATE_ZOOM, generateZoom, "Zoom", FLOAT, 100.0f) \
X(GENERATE_START_POSITION, generateStartPosition, STRING_UNDEFINED, IVEC2, {}) \
X(GENERATE_SIZE, generateSize, STRING_UNDEFINED, IVEC2, {64, 64}) \
X(GENERATE_PIVOT, generatePivot, STRING_UNDEFINED, IVEC2, {32, 32}) \
X(GENERATE_ROWS, generateRows, STRING_UNDEFINED, INT, 4) \
X(GENERATE_COLUMNS, generateColumns, STRING_UNDEFINED, INT, 4) \
X(GENERATE_COUNT, generateCount, STRING_UNDEFINED, INT, 16) \
X(GENERATE_DURATION, generateDuration, STRING_UNDEFINED, INT, 1) \
X(GENERATE_ZOOM, generateZoom, STRING_UNDEFINED, FLOAT, 100.0f) \
\
X(EDITOR_IS_GRID, editorIsGrid, "Grid", BOOL, true) \
X(EDITOR_IS_GRID_SNAP, editorIsGridSnap, "Snap", BOOL, true) \
X(EDITOR_IS_BORDER, editorIsBorder, "Border", BOOL, true) \
X(EDITOR_IS_TRANSPARENT, editorIsTransparent, "Transparent", BOOL, true) \
X(EDITOR_START_ZOOM, editorStartZoom, "Zoom", FLOAT, 200.0f) \
X(EDITOR_SIZE, editorSize, "Size", IVEC2_WH, {1200, 600}) \
X(EDITOR_GRID_SIZE, editorGridSize, "Grid Size", IVEC2, {32, 32}) \
X(EDITOR_GRID_OFFSET, editorGridOffset, "Offset", IVEC2, {32, 32}) \
X(EDITOR_GRID_COLOR, editorGridColor, "Color", VEC4, {1.0, 1.0, 1.0, 0.125}) \
X(EDITOR_BACKGROUND_COLOR, editorBackgroundColor, "Background Color", VEC3, {0.113, 0.184, 0.286}) \
X(EDITOR_IS_GRID, editorIsGrid, STRING_UNDEFINED, BOOL, true) \
X(EDITOR_IS_GRID_SNAP, editorIsGridSnap, STRING_UNDEFINED, BOOL, true) \
X(EDITOR_IS_BORDER, editorIsBorder, STRING_UNDEFINED, BOOL, true) \
X(EDITOR_IS_TRANSPARENT, editorIsTransparent, STRING_UNDEFINED, BOOL, true) \
X(EDITOR_START_ZOOM, editorStartZoom, STRING_UNDEFINED, FLOAT, 200.0f) \
X(EDITOR_SIZE, editorSize, STRING_UNDEFINED, IVEC2_WH, {1200, 600}) \
X(EDITOR_GRID_SIZE, editorGridSize, STRING_UNDEFINED, IVEC2, {32, 32}) \
X(EDITOR_GRID_OFFSET, editorGridOffset, STRING_UNDEFINED, IVEC2, {32, 32}) \
X(EDITOR_GRID_COLOR, editorGridColor, STRING_UNDEFINED, VEC4, {1.0, 1.0, 1.0, 0.125}) \
X(EDITOR_BACKGROUND_COLOR, editorBackgroundColor, STRING_UNDEFINED, VEC3, {0.113, 0.184, 0.286}) \
\
X(MERGE_TYPE, mergeType, "Type", INT, 0) \
X(MERGE_IS_DELETE_ANIMATIONS_AFTER, mergeIsDeleteAnimationsAfter, "Delete Animations After", BOOL, false) \
X(MERGE_TYPE, mergeType, STRING_UNDEFINED, INT, 0) \
X(MERGE_IS_DELETE_ANIMATIONS_AFTER, mergeIsDeleteAnimationsAfter, STRING_UNDEFINED, BOOL, false) \
\
X(BAKE_INTERVAL, bakeInterval, "Interval", INT, 1) \
X(BAKE_IS_ROUND_SCALE, bakeIsRoundScale, "Round Scale", BOOL, true) \
X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, "Round Rotation", BOOL, true) \
X(BAKE_INTERVAL, bakeInterval, STRING_UNDEFINED, INT, 1) \
X(BAKE_IS_ROUND_SCALE, bakeIsRoundScale, STRING_UNDEFINED, BOOL, true) \
X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, STRING_UNDEFINED, BOOL, true) \
\
X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, "Add Item Type", INT, anm2::LAYER) \
X(TIMELINE_ADD_ITEM_LOCALITY, timelineAddItemLocale, "Add Item Locale", INT, types::locale::GLOBAL) \
X(TIMELINE_ADD_ITEM_SOURCE, timelineAddItemSource, "Add Item Source", INT, types::source::NEW) \
X(TIMELINE_IS_SHOW_UNUSED, timelineIsShowUnused, "##Show Unused", BOOL, true) \
X(TIMELINE_IS_ONLY_SHOW_LAYERS, timelineIsOnlyShowLayers, "##Only Show Layers", BOOL, false) \
X(TIMELINE_IS_SOUND, timelineIsSound, "Sound", BOOL, true) \
X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, STRING_UNDEFINED, INT, anm2::LAYER) \
X(TIMELINE_ADD_ITEM_LOCALITY, timelineAddItemLocale, STRING_UNDEFINED, INT, types::locale::GLOBAL) \
X(TIMELINE_ADD_ITEM_SOURCE, timelineAddItemSource, STRING_UNDEFINED, INT, types::source::NEW) \
X(TIMELINE_IS_SHOW_UNUSED, timelineIsShowUnused, STRING_UNDEFINED, BOOL, true) \
X(TIMELINE_IS_ONLY_SHOW_LAYERS, timelineIsOnlyShowLayers, STRING_UNDEFINED, BOOL, false) \
X(TIMELINE_IS_SOUND, timelineIsSound, STRING_UNDEFINED, BOOL, true) \
\
X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, "Enabled", BOOL, false) \
X(ONIONSKIN_BEFORE_COUNT, onionskinBeforeCount, "Frames", INT, 0) \
X(ONIONSKIN_AFTER_COUNT, onionskinAfterCount, "Frames", INT, 0) \
X(ONIONSKIN_BEFORE_COLOR, onionskinBeforeColor, "Color", VEC3, types::color::RED) \
X(ONIONSKIN_AFTER_COLOR, onionskinAfterColor, "Color", VEC3, types::color::BLUE) \
X(ONIONSKIN_MODE, onionskinMode, "Mode", INT, (int)types::OnionskinMode::TIME) \
X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, STRING_UNDEFINED, BOOL, false) \
X(ONIONSKIN_BEFORE_COUNT, onionskinBeforeCount, STRING_UNDEFINED, INT, 0) \
X(ONIONSKIN_AFTER_COUNT, onionskinAfterCount, STRING_UNDEFINED, INT, 0) \
X(ONIONSKIN_BEFORE_COLOR, onionskinBeforeColor, STRING_UNDEFINED, VEC3, types::color::RED) \
X(ONIONSKIN_AFTER_COLOR, onionskinAfterColor, STRING_UNDEFINED, VEC3, types::color::BLUE) \
X(ONIONSKIN_MODE, onionskinMode, STRING_UNDEFINED, INT, (int)types::OnionskinMode::TIME) \
\
X(TOOL, tool, "##Tool", INT, 0) \
X(TOOL_COLOR, toolColor, "##Color", VEC4, {1.0, 1.0, 1.0, 1.0}) \
X(TOOL, tool, STRING_UNDEFINED, INT, 0) \
X(TOOL_COLOR, toolColor, STRING_UNDEFINED, VEC4, {1.0, 1.0, 1.0, 1.0}) \
\
X(RENDER_TYPE, renderType, "Output", INT, render::GIF) \
X(RENDER_PATH, renderPath, "Path", STRING, "./output.gif") \
X(RENDER_ROWS, renderRows, "Rows", INT, 0) \
X(RENDER_COLUMNS, renderColumns, "Columns", INT, 0) \
X(RENDER_FORMAT, renderFormat, "Format", STRING, "{}.png") \
X(RENDER_IS_RAW_ANIMATION, renderIsRawAnimation, "Raw Animation", BOOL, true) \
X(RENDER_SCALE, renderScale, "Scale", FLOAT, 1.0f) \
X(RENDER_FFMPEG_PATH, renderFFmpegPath, "FFmpeg Path", STRING, FFMPEG_PATH_DEFAULT)
X(RENDER_TYPE, renderType, STRING_UNDEFINED, INT, render::GIF) \
X(RENDER_PATH, renderPath, STRING_UNDEFINED, STRING, "./output.gif") \
X(RENDER_ROWS, renderRows, STRING_UNDEFINED, INT, 0) \
X(RENDER_COLUMNS, renderColumns, STRING_UNDEFINED, INT, 0) \
X(RENDER_FORMAT, renderFormat, STRING_UNDEFINED, STRING, "{}.png") \
X(RENDER_IS_RAW_ANIMATION, renderIsRawAnimation, STRING_UNDEFINED, BOOL, true) \
X(RENDER_SCALE, renderScale, STRING_UNDEFINED, FLOAT, 1.0f) \
X(RENDER_FFMPEG_PATH, renderFFmpegPath, STRING_UNDEFINED, STRING, FFMPEG_PATH_DEFAULT)
#define SETTINGS_SHORTCUTS \
/* Symbol / Name / String / Type / Default */ \
/* File */ \
X(SHORTCUT_NEW, shortcutNew, "New", STRING, "Ctrl+N") \
X(SHORTCUT_OPEN, shortcutOpen, "Open", STRING, "Ctrl+O") \
X(SHORTCUT_CLOSE, shortcutClose, "Close", STRING, "Ctrl+W") \
X(SHORTCUT_SAVE, shortcutSave, "Save", STRING, "Ctrl+S") \
X(SHORTCUT_SAVE_AS, shortcutSaveAs, "Save As", STRING, "Ctrl+Shift+S") \
X(SHORTCUT_EXIT, shortcutExit, "Exit", STRING, "Alt+F4") \
X(SHORTCUT_NEW, shortcutNew, SHORTCUT_STRING_NEW, STRING, "Ctrl+N") \
X(SHORTCUT_OPEN, shortcutOpen, SHORTCUT_STRING_OPEN, STRING, "Ctrl+O") \
X(SHORTCUT_CLOSE, shortcutClose, SHORTCUT_STRING_CLOSE, STRING, "Ctrl+W") \
X(SHORTCUT_SAVE, shortcutSave, SHORTCUT_STRING_SAVE, STRING, "Ctrl+S") \
X(SHORTCUT_SAVE_AS, shortcutSaveAs, SHORTCUT_STRING_SAVE_AS, STRING, "Ctrl+Shift+S") \
X(SHORTCUT_EXIT, shortcutExit, SHORTCUT_STRING_EXIT, STRING, "Alt+F4") \
/* Edit */ \
X(SHORTCUT_UNDO, shortcutUndo, "Undo", STRING, "Ctrl+Z") \
X(SHORTCUT_REDO, shortcutRedo, "Redo", STRING, "Ctrl+Shift+Z") \
X(SHORTCUT_CUT, shortcutCut, "Cut", STRING, "Ctrl+X") \
X(SHORTCUT_COPY, shortcutCopy, "Copy", STRING, "Ctrl+C") \
X(SHORTCUT_PASTE, shortcutPaste, "Paste", STRING, "Ctrl+V") \
X(SHORTCUT_DUPLICATE, shortcutDuplicate, "Duplicate", STRING, "Ctrl+J") \
X(SHORTCUT_ADD, shortcutAdd, "Add", STRING, "Insert") \
X(SHORTCUT_REMOVE, shortcutRemove, "Remove", STRING, "Delete") \
X(SHORTCUT_DEFAULT, shortcutDefault, "Default", STRING, "Home") \
X(SHORTCUT_MERGE, shortcutMerge, "Merge", STRING, "Ctrl+E") \
X(SHORTCUT_UNDO, shortcutUndo, SHORTCUT_STRING_UNDO, STRING, "Ctrl+Z") \
X(SHORTCUT_REDO, shortcutRedo, SHORTCUT_STRING_REDO, STRING, "Ctrl+Shift+Z") \
X(SHORTCUT_CUT, shortcutCut, SHORTCUT_STRING_CUT, STRING, "Ctrl+X") \
X(SHORTCUT_COPY, shortcutCopy, SHORTCUT_STRING_COPY, STRING, "Ctrl+C") \
X(SHORTCUT_PASTE, shortcutPaste, SHORTCUT_STRING_PASTE, STRING, "Ctrl+V") \
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_DEFAULT, shortcutDefault, SHORTCUT_STRING_DEFAULT, STRING, "Home") \
X(SHORTCUT_MERGE, shortcutMerge, SHORTCUT_STRING_MERGE, STRING, "Ctrl+E") \
/* Tools */ \
X(SHORTCUT_PAN, shortcutPan, "Pan", STRING, "P") \
X(SHORTCUT_MOVE, shortcutMove, "Move", STRING, "V") \
X(SHORTCUT_ROTATE, shortcutRotate, "Rotate", STRING, "R") \
X(SHORTCUT_SCALE, shortcutScale, "Scale", STRING, "S") \
X(SHORTCUT_CROP, shortcutCrop, "Crop", STRING, "C") \
X(SHORTCUT_DRAW, shortcutDraw, "Draw", STRING, "B") \
X(SHORTCUT_ERASE, shortcutErase, "Erase", STRING, "E") \
X(SHORTCUT_COLOR_PICKER, shortcutColorPicker, "Color Picker", STRING, "I") \
X(SHORTCUT_COLOR, shortcutColor, "Color", STRING, "X") \
X(SHORTCUT_PAN, shortcutPan, SHORTCUT_STRING_PAN, STRING, "P") \
X(SHORTCUT_MOVE, shortcutMove, SHORTCUT_STRING_MOVE, STRING, "V") \
X(SHORTCUT_ROTATE, shortcutRotate, SHORTCUT_STRING_ROTATE, STRING, "R") \
X(SHORTCUT_SCALE, shortcutScale, SHORTCUT_STRING_SCALE, STRING, "S") \
X(SHORTCUT_CROP, shortcutCrop, SHORTCUT_STRING_CROP, STRING, "C") \
X(SHORTCUT_DRAW, shortcutDraw, SHORTCUT_STRING_DRAW, STRING, "B") \
X(SHORTCUT_ERASE, shortcutErase, SHORTCUT_STRING_ERASE, STRING, "E") \
X(SHORTCUT_COLOR_PICKER, shortcutColorPicker, SHORTCUT_STRING_COLOR_PICKER, STRING, "I") \
X(SHORTCUT_COLOR, shortcutColor, SHORTCUT_STRING_COLOR, STRING, "X") \
/* View */ \
X(SHORTCUT_CENTER_VIEW, shortcutCenterView, "Center View", STRING, "Home") \
X(SHORTCUT_FIT, shortcutFit, "Fit", STRING, "F") \
X(SHORTCUT_ZOOM_IN, shortcutZoomIn, "Zoom In", STRING, "Ctrl+Equal") \
X(SHORTCUT_ZOOM_OUT, shortcutZoomOut, "Zoom Out", STRING, "Ctrl+Minus") \
X(SHORTCUT_CENTER_VIEW, shortcutCenterView, SHORTCUT_STRING_CENTER_VIEW, STRING, "Home") \
X(SHORTCUT_FIT, shortcutFit, SHORTCUT_STRING_FIT, STRING, "F") \
X(SHORTCUT_ZOOM_IN, shortcutZoomIn, SHORTCUT_STRING_ZOOM_IN, STRING, "Ctrl+Equal") \
X(SHORTCUT_ZOOM_OUT, shortcutZoomOut, SHORTCUT_STRING_ZOOM_OUT, STRING, "Ctrl+Minus") \
/* Timeline / Playback */ \
X(SHORTCUT_PLAY_PAUSE, shortcutPlayPause, "Play/Pause", STRING, "Space") \
X(SHORTCUT_MOVE_PLAYHEAD_BACK, shortcutMovePlayheadBack, "Playhead Back", STRING, "Comma") \
X(SHORTCUT_MOVE_PLAYHEAD_FORWARD, shortcutMovePlayheadForward, "Playhead Forward", STRING, "Period") \
X(SHORTCUT_SHORTEN_FRAME, shortcutShortenFrame, "Shorten Frame", STRING, "F4") \
X(SHORTCUT_EXTEND_FRAME, shortcutExtendFrame, "Extend Frame", STRING, "F5") \
X(SHORTCUT_INSERT_FRAME, shortcutInsertFrame, "Insert Frame", STRING, "F6") \
X(SHORTCUT_PREVIOUS_FRAME, shortcutPreviousFrame, "Previous Frame", STRING, "F7") \
X(SHORTCUT_NEXT_FRAME, shortcutNextFrame, "Next Frame", STRING, "F8") \
X(SHORTCUT_PLAY_PAUSE, shortcutPlayPause, SHORTCUT_STRING_PLAY_PAUSE, STRING, "Space") \
X(SHORTCUT_MOVE_PLAYHEAD_BACK, shortcutMovePlayheadBack, SHORTCUT_STRING_PLAYHEAD_BACK, STRING, "Comma") \
X(SHORTCUT_MOVE_PLAYHEAD_FORWARD, shortcutMovePlayheadForward, SHORTCUT_STRING_PLAYHEAD_FORWARD, STRING, "Period") \
X(SHORTCUT_SHORTEN_FRAME, shortcutShortenFrame, SHORTCUT_STRING_SHORTEN_FRAME, STRING, "F4") \
X(SHORTCUT_EXTEND_FRAME, shortcutExtendFrame, SHORTCUT_STRING_EXTEND_FRAME, STRING, "F5") \
X(SHORTCUT_INSERT_FRAME, shortcutInsertFrame, SHORTCUT_STRING_INSERT_FRAME, STRING, "F6") \
X(SHORTCUT_PREVIOUS_FRAME, shortcutPreviousFrame, SHORTCUT_STRING_PREVIOUS_FRAME, STRING, "F7") \
X(SHORTCUT_NEXT_FRAME, shortcutNextFrame, SHORTCUT_STRING_NEXT_FRAME, STRING, "F8") \
/* Toggles */ \
X(SHORTCUT_ONIONSKIN, shortcutOnionskin, "Onionskin", STRING, "O")
X(SHORTCUT_ONIONSKIN, shortcutOnionskin, SHORTCUT_STRING_ONIONSKIN, STRING, "O")
#define SETTINGS_WINDOWS \
/* Symbol / Name / String / Type / Default */ \
X(WINDOW_ANIMATIONS, windowIsAnimations, "Animations", BOOL, true) \
X(WINDOW_ANIMATION_PREVIEW, windowIsAnimationPreview, "Animation Preview", BOOL, true) \
X(WINDOW_EVENTS, windowIsEvents, "Events", BOOL, true) \
X(WINDOW_FRAME_PROPERTIES, windowIsFrameProperties, "Frame Properties", BOOL, true) \
X(WINDOW_LAYERS, windowIsLayers, "Layers", BOOL, true) \
X(WINDOW_NULLS, windowIsNulls, "Nulls", BOOL, true) \
X(WINDOW_ONIONSKIN, windowIsOnionskin, "Onionskin", BOOL, true) \
X(WINDOW_PREVIEW, windowIsSpritesheets, "Spritesheets", BOOL, true) \
X(WINDOW_SOUNDS, windowIsSounds, "Sounds", BOOL, true) \
X(WINDOW_SPRITESHEET_EDITOR, windowIsSpritesheetEditor, "Spritesheet Editor", BOOL, true) \
X(WINDOW_TIMELINE, windowIsTimeline, "Timeline", BOOL, true) \
X(WINDOW_TOOLS, windowIsTools, "Tools", BOOL, true)
X(WINDOW_ANIMATIONS, windowIsAnimations, LABEL_ANIMATIONS_WINDOW, BOOL, true) \
X(WINDOW_ANIMATION_PREVIEW, windowIsAnimationPreview, LABEL_ANIMATION_PREVIEW_WINDOW, BOOL, true) \
X(WINDOW_EVENTS, windowIsEvents, LABEL_EVENTS_WINDOW, BOOL, true) \
X(WINDOW_FRAME_PROPERTIES, windowIsFrameProperties, LABEL_FRAME_PROPERTIES_WINDOW, BOOL, true) \
X(WINDOW_LAYERS, windowIsLayers, LABEL_LAYERS_WINDOW, BOOL, true) \
X(WINDOW_NULLS, windowIsNulls, LABEL_NULLS_WINDOW, BOOL, true) \
X(WINDOW_ONIONSKIN, windowIsOnionskin, LABEL_ONIONSKIN_WINDOW, BOOL, true) \
X(WINDOW_PREVIEW, windowIsSpritesheets, LABEL_SPRITESHEETS_WINDOW, BOOL, true) \
X(WINDOW_SOUNDS, windowIsSounds, LABEL_SOUNDS_WINDOW, BOOL, true) \
X(WINDOW_SPRITESHEET_EDITOR, windowIsSpritesheetEditor, LABEL_SPRITESHEET_EDITOR_WINDOW, BOOL, true) \
X(WINDOW_TIMELINE, windowIsTimeline, LABEL_TIMELINE_WINDOW, BOOL, true) \
X(WINDOW_TOOLS, windowIsTools, LABEL_TOOLS_WINDOW, BOOL, true)
enum ShortcutType
{
@@ -241,8 +243,8 @@ namespace anm2ed
void save(const std::string&, const std::string&);
};
constexpr const char* SHORTCUT_STRINGS[] = {
#define X(symbol, name, string, type, ...) string,
constexpr StringType SHORTCUT_STRING_TYPES[] = {
#define X(symbol, name, stringType, type, ...) stringType,
SETTINGS_SHORTCUTS
#undef X
};
@@ -262,8 +264,8 @@ namespace anm2ed
WINDOW_COUNT
};
constexpr const char* WINDOW_STRINGS[] = {
#define X(symbol, name, string, type, ...) string,
constexpr StringType WINDOW_STRING_TYPES[] = {
#define X(symbol, name, stringType, type, ...) stringType,
SETTINGS_WINDOWS
#undef X
};

View File

@@ -6,6 +6,8 @@
#include <imgui/backends/imgui_impl_sdl3.h>
#include "filesystem_.h"
#include "log.h"
#include "strings.h"
#include "toast.h"
using namespace anm2ed::imgui;
@@ -65,7 +67,21 @@ namespace anm2ed
if (auto document = manager.get())
document->spritesheet_add(droppedFile);
else
toasts.info("Failed to add spritesheet! Open a document first.");
{
toasts.push(localize.get(TOAST_ADD_SPRITESHEET_FAILED));
logger.warning(localize.get(TOAST_ADD_SPRITESHEET_FAILED, anm2ed::ENGLISH));
}
}
else if (filesystem::path_is_extension(droppedFile, "wav") ||
filesystem::path_is_extension(droppedFile, "ogg"))
{
if (auto document = manager.get())
document->sound_add(droppedFile);
else
{
toasts.push(localize.get(TOAST_ADD_SOUND_FAILED));
logger.warning(localize.get(TOAST_ADD_SOUND_FAILED, anm2ed::ENGLISH));
}
}
break;
}

View File

@@ -2,6 +2,7 @@
#include "icon.h"
#include "settings.h"
#include "strings.h"
namespace anm2ed::tool
{
@@ -35,47 +36,35 @@ namespace anm2ed::tool
ShortcutType shortcut{};
AreaType areaType;
const char* label{};
const char* tooltip{};
StringType tooltip{};
};
constexpr Info INFO[] = {
{ImGuiMouseCursor_Hand, resource::icon::PAN, SHORTCUT_PAN, ALL, "##Pan",
"Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou can also use the middle mouse button to "
"pan at any time."},
{ImGuiMouseCursor_Hand, resource::icon::PAN, SHORTCUT_PAN, ALL, "##Pan", TOOLTIP_TOOL_PAN},
{ImGuiMouseCursor_ResizeAll, resource::icon::MOVE, SHORTCUT_MOVE, ALL, "##Move",
"Use the move tool.\nAnimation Preview: Will move the position of the frame."
"\nSpritesheet Editor: Will move the pivot, and holding right click will use the Crop functionality instead."
"\nUse mouse or directional keys to change the value."},
{ImGuiMouseCursor_ResizeAll, resource::icon::MOVE, SHORTCUT_MOVE, ALL, "##Move", TOOLTIP_TOOL_MOVE},
{ImGuiMouseCursor_Arrow, resource::icon::ROTATE, SHORTCUT_ROTATE, ANIMATION_PREVIEW, "##Rotate",
"Use the rotate tool.\nWill rotate the selected item as the cursor is dragged, or directional keys are "
"pressed.\n(Animation Preview only.)"},
TOOLTIP_TOOL_ROTATE},
{ImGuiMouseCursor_ResizeNESW, resource::icon::SCALE, SHORTCUT_SCALE, ANIMATION_PREVIEW, "##Scale",
"Use the scale tool.\nWill scale the selected item as the cursor is dragged, or directional keys are "
"pressed.\nHold SHIFT to lock scaling to one dimension.\n(Animation Preview only.)"},
TOOLTIP_TOOL_SCALE},
{ImGuiMouseCursor_Arrow, resource::icon::CROP, SHORTCUT_CROP, SPRITESHEET_EDITOR, "##Crop",
"Use the crop tool.\nWill produce a crop rectangle based on how the cursor is dragged, or directional keys are "
"pressed.\nHold CTRL with arrow keys to change position."
"\nHolding right click will use the Move tool's functionality."
"\n(Spritesheet Editor only.)"},
{ImGuiMouseCursor_Arrow, resource::icon::CROP, SHORTCUT_CROP, SPRITESHEET_EDITOR, "##Crop", TOOLTIP_TOOL_CROP},
{ImGuiMouseCursor_Arrow, resource::icon::DRAW, SHORTCUT_DRAW, SPRITESHEET_EDITOR, "##Draw",
"Draws pixels onto the selected spritesheet, with the current color.\n(Spritesheet Editor only.)"},
TOOLTIP_TOOL_DRAW},
{ImGuiMouseCursor_Arrow, resource::icon::ERASE, SHORTCUT_ERASE, SPRITESHEET_EDITOR, "##Erase",
"Erases pixels from the selected spritesheet.\n(Spritesheet Editor only.)"},
TOOLTIP_TOOL_ERASE},
{ImGuiMouseCursor_Arrow, resource::icon::COLOR_PICKER, SHORTCUT_COLOR_PICKER, SPRITESHEET_EDITOR,
"##Color Picker", "Selects a color from the canvas.\n(Spritesheet Editor only.)"},
{ImGuiMouseCursor_Arrow, resource::icon::COLOR_PICKER, SHORTCUT_COLOR_PICKER, SPRITESHEET_EDITOR, "##Color Picker",
TOOLTIP_TOOL_COLOR_PICKER},
{ImGuiMouseCursor_None, resource::icon::UNDO, SHORTCUT_UNDO, ALL, "##Undo", "Undoes the last action."},
{ImGuiMouseCursor_None, resource::icon::UNDO, SHORTCUT_UNDO, ALL, "##Undo", TOOLTIP_TOOL_UNDO},
{ImGuiMouseCursor_None, resource::icon::REDO, SHORTCUT_REDO, ALL, "##Redo", "Redoes the last action."},
{ImGuiMouseCursor_None, resource::icon::REDO, SHORTCUT_REDO, ALL, "##Redo", TOOLTIP_TOOL_REDO},
{ImGuiMouseCursor_None, resource::icon::NONE, SHORTCUT_COLOR, ALL, "##Color",
"Selects the color to be used for drawing.\n(Spritesheet Editor only.)"},
{ImGuiMouseCursor_None, resource::icon::NONE, SHORTCUT_COLOR, ALL, "##Color", TOOLTIP_TOOL_COLOR},
};
}
}

View File

@@ -4,12 +4,14 @@
#include <glm/gtc/type_ptr.hpp>
#include <imgui/imgui.h>
#include "strings.h"
namespace anm2ed::types::theme
{
#define THEMES \
X(LIGHT, "Light") \
X(DARK, "Dark") \
X(CLASSIC, "ImGui Classic")
X(LIGHT, LABEL_THEME_LIGHT) \
X(DARK, LABEL_THEME_DARK) \
X(CLASSIC, LABEL_THEME_CLASSIC)
enum Type
{
@@ -19,7 +21,7 @@ namespace anm2ed::types::theme
COUNT
};
constexpr const char* STRINGS[] = {
constexpr StringType STRINGS[] = {
#define X(symbol, string) string,
THEMES
#undef X