more minor polish there and here

This commit is contained in:
2025-11-11 16:07:02 -05:00
parent 2a671e2623
commit d9a05947c0
20 changed files with 221 additions and 81 deletions

2
.vscode/launch.json vendored
View File

@@ -2,7 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "Debug ANM2Ed",
"name": "Debug",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/anm2ed",

View File

@@ -27,6 +27,7 @@ set(SDL_SENSOR OFF CACHE BOOL "" FORCE)
set(SDL_HIDAPI OFF CACHE BOOL "" FORCE)
set(SDL_CAMERA OFF CACHE BOOL "" FORCE)
set(SDL_TRAY OFF CACHE BOOL "" FORCE)
set(SDL_VULKAN OFF CACHE BOOL "" FORCE)
add_subdirectory(external/SDL EXCLUDE_FROM_ALL)
set(SDLMIXER_DEPS_SHARED OFF CACHE BOOL "" FORCE)

View File

@@ -12,7 +12,7 @@ using namespace glm;
namespace anm2ed::anm2
{
Anm2::Anm2() { info.createdOn = time::get("%d-%B-%Y %I:%M:%S"); }
Anm2::Anm2() { info.createdOn = time::get("%m/%d/%Y %I:%M:%S %p"); }
Anm2::Anm2(const std::string& path, std::string* errorString)
{
@@ -74,4 +74,4 @@ namespace anm2ed::anm2
if (vector::in_bounds(item->frames, frameIndex)) return &item->frames[frameIndex];
return nullptr;
}
}
}

View File

@@ -54,10 +54,13 @@ namespace anm2ed::anm2
event.serialize(document, eventsElement, id);
element->InsertEndChild(eventsElement);
auto soundsElement = document.NewElement("Sounds");
for (auto& [id, sound] : sounds)
sound.serialize(document, soundsElement, id);
element->InsertEndChild(soundsElement);
if (!sounds.empty())
{
auto soundsElement = document.NewElement("Sounds");
for (auto& [id, sound] : sounds)
sound.serialize(document, soundsElement, id);
element->InsertEndChild(soundsElement);
}
parent->InsertEndChild(element);
}

View File

@@ -2,7 +2,6 @@
#include <map>
#include "anm2_type.h"
#include "event.h"
#include "layer.h"
#include "null.h"

View File

@@ -240,25 +240,25 @@ namespace anm2ed::anm2
{
if (!vector::in_bounds(frames, index)) return;
Frame& frame = frames[index];
if (frame.duration == FRAME_DURATION_MIN) return;
auto original = frames[index];
if (original.duration == FRAME_DURATION_MIN) return;
Frame frameNext = vector::in_bounds(frames, index + 1) ? frames[index + 1] : frame;
auto nextFrame = vector::in_bounds(frames, index + 1) ? frames[index + 1] : original;
int duration{};
int i = index;
while (duration < frame.duration)
while (duration < original.duration)
{
Frame baked = frame;
float interpolation = (float)duration / frame.duration;
baked.duration = std::min(interval, frame.duration - duration);
baked.isInterpolated = (i == index) ? frame.isInterpolated : false;
baked.rotation = glm::mix(frame.rotation, frameNext.rotation, interpolation);
baked.position = glm::mix(frame.position, frameNext.position, interpolation);
baked.scale = glm::mix(frame.scale, frameNext.scale, interpolation);
baked.colorOffset = glm::mix(frame.colorOffset, frameNext.colorOffset, interpolation);
baked.tint = glm::mix(frame.tint, frameNext.tint, interpolation);
Frame baked = original;
float interpolation = (float)duration / original.duration;
baked.duration = std::min(interval, original.duration - duration);
baked.isInterpolated = (i == index) ? original.isInterpolated : false;
baked.rotation = glm::mix(original.rotation, nextFrame.rotation, interpolation);
baked.position = glm::mix(original.position, nextFrame.position, interpolation);
baked.scale = glm::mix(original.scale, nextFrame.scale, interpolation);
baked.colorOffset = glm::mix(original.colorOffset, nextFrame.colorOffset, interpolation);
baked.tint = glm::mix(original.tint, nextFrame.tint, interpolation);
if (isRoundScale) baked.scale = vec2(ivec2(baked.scale));
if (isRoundRotation) baked.rotation = (int)baked.rotation;

View File

@@ -2,14 +2,12 @@
#include <utility>
#include "filesystem_.h"
#include "log.h"
#include "toast.h"
using namespace anm2ed::anm2;
using namespace anm2ed::imgui;
using namespace anm2ed::types;
using namespace anm2ed::util;
using namespace glm;
@@ -17,10 +15,11 @@ namespace anm2ed
{
Document::Document(const std::string& path, bool isNew, std::string* errorString)
{
if (!filesystem::path_is_exist(path)) return;
if (isNew)
{
anm2 = anm2::Anm2();
if (!save(path)) return;
}
else
{
anm2 = Anm2(path, errorString);
@@ -38,7 +37,7 @@ namespace anm2ed
merge(current.merge), event(current.event), layer(current.layer), null(current.null), sound(current.sound),
spritesheet(current.spritesheet), frames(current.frames), message(current.message),
previewZoom(other.previewZoom), previewPan(other.previewPan), editorPan(other.editorPan),
editorZoom(other.editorZoom), overlayIndex(other.overlayIndex), saveHash(other.saveHash),
editorZoom(other.editorZoom), overlayIndex(other.overlayIndex), hash(other.hash), saveHash(other.saveHash),
autosaveHash(other.autosaveHash), lastAutosaveTime(other.lastAutosaveTime), isOpen(other.isOpen),
isForceDirty(other.isForceDirty), isAnimationPreviewSet(other.isAnimationPreviewSet),
isSpritesheetEditorSet(other.isSpritesheetEditorSet)

View File

@@ -17,6 +17,7 @@
#include "types.h"
#include "icon.h"
#include "toast.h"
using namespace anm2ed::resource;
using namespace anm2ed::types;
@@ -60,7 +61,12 @@ namespace anm2ed::imgui
ImGui::EndMenu();
}
if (ImGui::MenuItem("Save", settings.shortcutSave.c_str(), false, document)) manager.save();
if (ImGui::MenuItem("Save", settings.shortcutSave.c_str(), false, document))
{
if (settings.fileIsWarnOverwrite) overwritePopup.open();
manager.save();
}
if (ImGui::MenuItem("Save As", settings.shortcutSaveAs.c_str(), false, document))
dialog.file_save(dialog::ANM2_SAVE);
if (ImGui::MenuItem("Explore XML Location", nullptr, false, document))
@@ -275,6 +281,11 @@ namespace anm2ed::imgui
input_int_range("Time (minutes)", editSettings.fileAutosaveTime, 0, 10);
ImGui::SetItemTooltip("If changed, will autosave documents using this interval.");
ImGui::EndDisabled();
ImGui::SeparatorText("Options");
ImGui::Checkbox("Overwrite Warning", &editSettings.fileIsWarnOverwrite);
ImGui::SetItemTooltip("A warning will be shown when saving a file.");
}
ImGui::EndChild();
@@ -297,7 +308,7 @@ namespace anm2ed::imgui
ImGui::SeparatorText("Zoom");
input_float_range("Step", editSettings.viewZoomStep, 10.0f, 250.0f, 10.0f, 10.0f, "%.0f");
input_float_range("Step", editSettings.viewZoomStep, 10.0f, 250.0f, 10.0f, 10.0f, "%.0f%%");
ImGui::SetItemTooltip("When zooming in/out with mouse or shortcut, this value will be used.");
}
ImGui::EndChild();
@@ -494,11 +505,77 @@ namespace anm2ed::imgui
if (ImGui::Button("Render", widgetSize))
{
manager.isRecordingStart = true;
playback.time = start;
playback.isPlaying = true;
renderPopup.close();
manager.progressPopup.open();
bool canStart = true;
auto warn_and_close = [&](const std::string& message)
{
toasts.warning(message);
renderPopup.close();
canStart = false;
};
auto ffmpegPathValid = [&]() -> bool
{
if (ffmpegPath.empty()) return false;
std::error_code ec{};
std::filesystem::path ffmpeg(ffmpegPath);
if (!std::filesystem::exists(ffmpeg, ec) || !std::filesystem::is_regular_file(ffmpeg, ec)) return false;
#ifdef _WIN32
auto ext = ffmpeg.extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { return (char)std::tolower(c); });
if (ext != ".exe") return false;
return true;
#else
auto permMask = std::filesystem::status(ffmpeg, ec).permissions();
using std::filesystem::perms;
if (permMask == perms::unknown) return true;
auto has_exec = [&](perms p)
{
return (perms::none != (p & perms::owner_exec)) || (perms::none != (p & perms::group_exec)) ||
(perms::none != (p & perms::others_exec));
};
return has_exec(permMask);
#endif
};
if (!ffmpegPathValid()) warn_and_close("Invalid FFmpeg executable. Please set a valid FFmpeg path.");
if (canStart)
{
std::error_code ec{};
if (type == render::PNGS)
{
if (path.empty())
warn_and_close("Select an output directory for PNG exports.");
else
{
std::filesystem::path directory(path);
if (!std::filesystem::exists(directory, ec) || !std::filesystem::is_directory(directory, ec))
warn_and_close("PNG exports require a valid directory.");
}
}
else
{
std::filesystem::path output(path);
auto parent = output.parent_path();
auto parentInvalid =
!parent.empty() && (!std::filesystem::exists(parent, ec) || !std::filesystem::is_directory(parent, ec));
if (path.empty() || std::filesystem::is_directory(output, ec) || parentInvalid)
{
output = std::filesystem::path("output").replace_extension(render::EXTENSIONS[type]);
path = output.string();
warn_and_close(std::format("Invalid output file. Using default path: {}", path));
}
}
}
if (canStart)
{
manager.isRecordingStart = true;
playback.time = start;
playback.isPlaying = true;
renderPopup.close();
manager.progressPopup.open();
}
}
ImGui::SameLine();
@@ -705,13 +782,38 @@ namespace anm2ed::imgui
ImGui::EndPopup();
}
overwritePopup.trigger();
if (ImGui::BeginPopupModal(overwritePopup.label, &overwritePopup.isOpen, ImGuiWindowFlags_NoResize))
{
ImGui::Text("Are you sure? This will overwrite the existing file.");
auto widgetSize = widget_size_with_row_get(2);
if (ImGui::Button("Yes", widgetSize))
{
manager.save();
overwritePopup.close();
}
ImGui::SameLine();
if (ImGui::Button("No", widgetSize)) overwritePopup.close();
ImGui::EndPopup();
}
if (resources.music.is_playing() && !aboutPopup.isOpen) resources.music.stop();
aboutPopup.end();
if (shortcut(manager.chords[SHORTCUT_NEW], shortcut::GLOBAL)) dialog.file_save(dialog::ANM2_NEW);
if (shortcut(manager.chords[SHORTCUT_OPEN], shortcut::GLOBAL)) dialog.file_open(dialog::ANM2_OPEN);
if (shortcut(manager.chords[SHORTCUT_SAVE], shortcut::GLOBAL)) manager.save();
if (shortcut(manager.chords[SHORTCUT_SAVE], shortcut::GLOBAL))
{
if (settings.fileIsWarnOverwrite) overwritePopup.open();
manager.save();
}
if (shortcut(manager.chords[SHORTCUT_SAVE_AS], shortcut::GLOBAL)) dialog.file_save(dialog::ANM2_SAVE);
if (shortcut(manager.chords[SHORTCUT_EXIT], shortcut::GLOBAL)) isQuitting = true;
}

View File

@@ -14,6 +14,7 @@ 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")};

View File

@@ -48,7 +48,8 @@ namespace anm2ed::imgui
{
if (auto trigger = animation->triggers.frame_generate(playback.time, anm2::TRIGGER);
trigger.is_visible(anm2::TRIGGER))
if (anm2.content.sounds.contains(trigger.soundID)) anm2.content.sounds[trigger.soundID].audio.play(mixer);
if (anm2.content.sounds.contains(trigger.soundID))
anm2.content.sounds[trigger.soundID].audio.play(false, mixer);
}
}
@@ -132,7 +133,7 @@ namespace anm2ed::imgui
{
if (auto rect = animation->rect(isRootTransform); rect != vec4(-1.0f))
{
size_set(vec2(rect.w, rect.z) * scale);
size_set(vec2(rect.z, rect.w) * scale);
set_to_rect(zoom, pan, rect);
}
}
@@ -583,6 +584,8 @@ namespace anm2ed::imgui
ImGui::ProgressBar(progress);
ImGui::Text("Once recording is complete, rendering may take some time.\nPlease be patient...");
if (ImGui::Button("Cancel", ImVec2(ImGui::GetContentRegionAvail().x, 0)))
{
playback.isPlaying = false;

View File

@@ -5,7 +5,6 @@
#include "audio_stream.h"
#include "canvas.h"
#include "manager.h"
#include "render.h"
#include "resources.h"
#include "settings.h"

View File

@@ -106,22 +106,26 @@ namespace anm2ed::imgui
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
auto viewport = ImGui::GetMainViewport();
auto textureSize = texture.size.x * texture.size.y > (viewport->Size.x * viewport->Size.y) * 0.5f
? to_vec2(viewport->Size) * 0.5f
: vec2(texture.size);
auto aspectRatio = (float)texture.size.x / texture.size.y;
if (textureSize.x / textureSize.y > aspectRatio)
textureSize.x = textureSize.y * aspectRatio;
else
textureSize.y = textureSize.x / aspectRatio;
auto textWidth = ImGui::CalcTextSize(path).x;
auto tooltipPadding = style.WindowPadding.x * 4.0f;
auto minWidth = textureSize.x + style.ItemSpacing.x + textWidth + tooltipPadding;
ImGui::SetNextWindowSize(ImVec2(minWidth, 0), ImGuiCond_Appearing);
if (ImGui::BeginItemTooltip())
{
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
auto viewport = ImGui::GetMainViewport();
auto textureSize = texture.size.x * texture.size.y > (viewport->Size.x * viewport->Size.y) * 0.5f
? to_vec2(viewport->Size) * 0.5f
: vec2(texture.size);
auto aspectRatio = (float)texture.size.x / texture.size.y;
if (textureSize.x / textureSize.y > aspectRatio)
textureSize.x = textureSize.y * aspectRatio;
else
textureSize.y = textureSize.x / aspectRatio;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
if (ImGui::BeginChild("##Spritesheet Tooltip Image Child", to_imvec2(textureSize),
ImGuiChildFlags_Borders))
@@ -148,7 +152,7 @@ namespace anm2ed::imgui
ImGui::PopStyleVar(2);
auto imageSize = to_imvec2(vec2(spritesheetChildSize.y));
auto aspectRatio = (float)texture.size.x / texture.size.y;
aspectRatio = (float)texture.size.x / texture.size.y;
if (imageSize.x / imageSize.y > aspectRatio)
imageSize.x = imageSize.y * aspectRatio;
@@ -284,4 +288,4 @@ namespace anm2ed::imgui
}
ImGui::End();
}
}
}

View File

@@ -176,7 +176,17 @@ namespace anm2ed::imgui
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4());
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4());
if (ImGui::Selectable("##Item Button", false, ImGuiSelectableFlags_None, itemSize))
{
if (type == anm2::LAYER)
{
document.spritesheet.reference = anm2.content.layers[id].spritesheetID;
document.layer.selection = {id};
}
else if (type == anm2::NULL_)
document.null.selection = {id};
reference = {reference.animationIndex, type, id};
}
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered())
{
@@ -993,10 +1003,11 @@ 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->frameNum = animation->length());
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.");
ImGui::EndDisabled();
ImGui::SameLine();
@@ -1030,7 +1041,7 @@ namespace anm2ed::imgui
auto createdBy = anm2.info.createdBy;
ImGui::SetNextItemWidth(widgetSize.x);
if (input_text_string("Author", &createdBy))
DOCUMENT_EDIT(document, "FPS", Document::ANIMATIONS, anm2.info.createdBy = createdBy);
DOCUMENT_EDIT(document, "Author", Document::ANIMATIONS, anm2.info.createdBy = createdBy);
ImGui::SetItemTooltip("Set the author of the document.");
ImGui::SameLine();
@@ -1239,9 +1250,10 @@ namespace anm2ed::imgui
if (ImGui::Button("Bake", widgetSize))
{
if (auto itemPtr = document.item_get())
DOCUMENT_EDIT(document, "Bake Frames", Document::FRAMES,
itemPtr->frames_bake(reference.frameIndex, interval, isRoundScale, isRoundRotation));
if (auto item = document.item_get())
for (auto i : frames.selection | std::views::reverse)
DOCUMENT_EDIT(document, "Bake Frames", Document::FRAMES,
item->frames_bake(i, interval, isRoundScale, isRoundRotation));
bakePopup.close();
}
ImGui::SetItemTooltip("Bake the selected frame(s) with the options selected.");

View File

@@ -39,15 +39,7 @@ namespace anm2ed
return;
}
if (isRecent)
{
recentFiles.erase(std::remove(recentFiles.begin(), recentFiles.end(), path), recentFiles.end());
recentFiles.insert(recentFiles.begin(), path);
if (recentFiles.size() > RECENT_LIMIT) recentFiles.resize(RECENT_LIMIT);
recent_files_write();
}
if (isRecent) recent_file_add(path);
selected = (int)documents.size() - 1;
pendingSelected = selected;
@@ -63,6 +55,7 @@ namespace anm2ed
std::string errorString{};
document->path = !path.empty() ? path : document->path.string();
document->save(document->path, &errorString);
recent_file_add(document->path);
}
}
@@ -170,6 +163,22 @@ namespace anm2ed
nullPropertiesPopup.close();
}
void Manager::recent_file_add(const std::string& path)
{
if (path.empty()) return;
std::error_code ec{};
if (!std::filesystem::exists(path, ec))
{
logger.warning(std::format("Skipping missing recent file: {}", path));
return;
}
recentFiles.erase(std::remove(recentFiles.begin(), recentFiles.end(), path), recentFiles.end());
recentFiles.insert(recentFiles.begin(), path);
if (recentFiles.size() > RECENT_LIMIT) recentFiles.resize(RECENT_LIMIT);
recent_files_write();
}
void Manager::recent_files_load()
{
auto path = recent_files_path_get();
@@ -189,6 +198,12 @@ namespace anm2ed
{
if (line.empty()) continue;
if (std::find(recentFiles.begin(), recentFiles.end(), line) != recentFiles.end()) continue;
std::error_code ec{};
if (!std::filesystem::exists(line, ec))
{
logger.warning(std::format("Skipping missing recent file: {}", line));
continue;
}
recentFiles.emplace_back(line);
}
}

View File

@@ -61,6 +61,7 @@ namespace anm2ed
void recent_files_load();
void recent_files_write();
void recent_files_clear();
void recent_file_add(const std::string&);
void autosave_files_load();
void autosave_files_open();

View File

@@ -34,6 +34,6 @@ namespace anm2ed::render
namespace anm2ed
{
bool animation_render(const std::string&, const std::string&, std::vector<resource::Texture>, AudioStream,
bool animation_render(const std::string&, const std::string&, std::vector<resource::Texture>&, AudioStream&,
render::Type, glm::ivec2, int);
}
}

View File

@@ -1,5 +1,10 @@
#include "audio_stream.h"
#if defined(__clang__) || defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
namespace anm2ed
{
void AudioStream::callback(void* userData, MIX_Mixer* mixer, const SDL_AudioSpec* spec, float* pcm, int samples)
@@ -8,15 +13,9 @@ namespace anm2ed
self->stream.insert(self->stream.end(), pcm, pcm + samples);
}
AudioStream::AudioStream(MIX_Mixer* mixer)
{
MIX_GetMixerFormat(mixer, &spec);
}
AudioStream::AudioStream(MIX_Mixer* mixer) { MIX_GetMixerFormat(mixer, &spec); }
void AudioStream::capture_begin(MIX_Mixer* mixer)
{
MIX_SetPostMixCallback(mixer, callback, this);
}
void AudioStream::capture_begin(MIX_Mixer* mixer) { MIX_SetPostMixCallback(mixer, callback, this); }
void AudioStream::capture_end(MIX_Mixer* mixer)
{

View File

@@ -66,12 +66,12 @@ namespace anm2ed::resource::icon
)";
constexpr auto SHOW_RECT_DATA = R"(
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="4" y="4" width="16" height="16" rx="0.5" stroke="#FFF" stroke-width="2" stroke-linejoin="round"/> <path d="M12 9.5v5M9.5 12h5" stroke="#FFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg>
)";
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="4" y="4" width="16" height="16" rx="0.5" stroke="#FFF" stroke-width="2" stroke-linejoin="round"/> <circle cx="12" cy="12" r="1" fill="#FFF" stroke="none"/> </svg>
)";
constexpr auto HIDE_RECT_DATA = R"(
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="4" y="4" width="16" height="16" rx="0.5" stroke="#FFF" stroke-width="2" stroke-linejoin="round"/> <path d="M12 9.5v5M9.5 12h5" stroke="#FFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M2 2L22 22" stroke="#FFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg>
)";
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect x="4" y="4" width="16" height="16" rx="0.5" stroke="#FFF" stroke-width="2" stroke-linejoin="round"/> <circle cx="12" cy="12" r="1" fill="#FFF" stroke="none"/> <path d="M2 2L22 22" stroke="#FFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg>
)";
constexpr auto ANIMATION_DATA = R"(
<svg viewBox="0 0 24 24" fill="#FFF" xmlns="http://www.w3.org/2000/svg"><path d="M5.99807 7L8.30747 3H11.9981L9.68867 7H5.99807ZM11.9981 7L14.3075 3H17.9981L15.6887 7H11.9981ZM17.9981 7L20.3075 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934C2 3.44476 2.45531 3 2.9918 3H5.99807L4 6.46076V19H20V7H17.9981Z"/></svg>

View File

@@ -47,6 +47,7 @@ namespace anm2ed
\
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(KEYBOARD_REPEAT_DELAY, keyboardRepeatDelay, "Repeat Delay", FLOAT, 0.300f) \
X(KEYBOARD_REPEAT_RATE, keyboardRepeatRate, "Repeat Rate", FLOAT, 0.050f) \

View File

@@ -32,7 +32,8 @@ namespace anm2ed::util::filesystem
std::filesystem::path path_lower_case_backslash_handle(std::filesystem::path& path)
{
if (path_is_exist(path)) return path;
if (path_is_exist(string::backslash_replace(path))) return path;
path = string::backslash_replace(path);
if (path_is_exist(path)) return path;
return string::to_lower(path);
}