Refactoring; structs for popups/multi-selects, additional popups in places

This commit is contained in:
2025-10-22 21:27:11 -04:00
parent 5b0f9a39c4
commit 87c2db2a77
31 changed files with 849 additions and 355 deletions

View File

@@ -1,8 +0,0 @@
{
"C_Cpp.formatting": "clangFormat",
"editor.formatOnSave": true,
"clang-format.style": "file",
"clangd.arguments": [
"--compile-commands-dir=build"
]
}

View File

@@ -3,33 +3,27 @@
#include <algorithm> #include <algorithm>
#include <ranges> #include <ranges>
#include "imgui.h" using namespace anm2ed::document;
using namespace anm2ed::document_manager;
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::resources; using namespace anm2ed::resources;
using namespace anm2ed::types; using namespace anm2ed::types;
namespace anm2ed::animations namespace anm2ed::animations
{ {
void Animations::update(DocumentManager& manager, Settings& settings, Resources& resources) void Animations::update(Document& document, int& documentIndex, Settings& settings, Resources& resources)
{ {
auto document = manager.get(); auto& anm2 = document.anm2;
auto& anm2 = document->anm2; auto& reference = document.reference;
auto& reference = document->reference; auto& selection = document.selectedAnimations;
auto& selection = document->selectedAnimations; storage.user_data_set(&selection);
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
if (ImGui::Begin("Animations", &settings.windowIsAnimations)) if (ImGui::Begin("Animations", &settings.windowIsAnimations))
{ {
auto childSize = imgui::size_with_footer_get(); auto childSize = imgui::size_without_footer_get();
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders)) if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
{ {
ImGuiMultiSelectIO* io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), storage.begin(anm2.animations.items.size());
anm2.animations.items.size());
storage.ApplyRequests(io);
for (auto [i, animation] : std::views::enumerate(anm2.animations.items)) for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
{ {
@@ -47,7 +41,7 @@ namespace anm2ed::animations
ImGui::PushFont(resources.fonts[font].get(), font::SIZE); ImGui::PushFont(resources.fonts[font].get(), font::SIZE);
ImGui::SetNextItemSelectionUserData(i); ImGui::SetNextItemSelectionUserData(i);
if (imgui::selectable_input_text(animation.name, if (imgui::selectable_input_text(animation.name,
std::format("###Document #{} Animation #{}", manager.selected, i), std::format("###Document #{} Animation #{}", documentIndex, i),
animation.name, isSelected)) animation.name, isSelected))
if (!isReferenced) reference = {(int)i}; if (!isReferenced) reference = {(int)i};
ImGui::PopFont(); ImGui::PopFont();
@@ -66,8 +60,8 @@ namespace anm2ed::animations
ImGui::PopFont(); ImGui::PopFont();
} }
ImGui::TextUnformatted(std::format("Length: {}", animation.frameNum).c_str()); ImGui::Text("Length: %d", animation.frameNum);
ImGui::TextUnformatted(std::format("Loop: {}", animation.isLoop).c_str()); ImGui::Text("Loop: %s", animation.isLoop ? "true" : "false");
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
@@ -102,18 +96,17 @@ namespace anm2ed::animations
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); storage.end();
storage.ApplyRequests(io);
} }
ImGui::EndChild(); ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(5); auto widgetSize = imgui::widget_size_with_row_get(5);
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize)) if (ImGui::Button("Add", widgetSize))
{ {
anm2::Animation animation; anm2::Animation animation;
if (anm2::Animation* referenceAnimation = document->animation_get()) if (anm2::Animation* referenceAnimation = document.animation_get())
{ {
for (auto [id, layerAnimation] : referenceAnimation->layerAnimations) for (auto [id, layerAnimation] : referenceAnimation->layerAnimations)
animation.layerAnimations[id] = anm2::Item(); animation.layerAnimations[id] = anm2::Item();
@@ -127,6 +120,7 @@ namespace anm2ed::animations
anm2.animations.items.insert(anm2.animations.items.begin() + index, animation); anm2.animations.items.insert(anm2.animations.items.begin() + index, animation);
selection = {index}; selection = {index};
reference = {index}; reference = {index};
document.change(change::ANIMATIONS);
} }
imgui::set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Add a new animation.", settings.shortcutAdd);
@@ -135,7 +129,7 @@ namespace anm2ed::animations
ImGui::BeginDisabled(selection.empty()); ImGui::BeginDisabled(selection.empty());
{ {
imgui::shortcut(settings.shortcutDuplicate, true); imgui::shortcut(settings.shortcutDuplicate);
if (ImGui::Button("Duplicate", widgetSize)) if (ImGui::Button("Duplicate", widgetSize))
{ {
auto duplicated = selection; auto duplicated = selection;
@@ -146,6 +140,7 @@ namespace anm2ed::animations
selection.insert(++duplicatedEnd); selection.insert(++duplicatedEnd);
selection.erase(id); selection.erase(id);
} }
document.change(change::ANIMATIONS);
} }
imgui::set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate); imgui::set_item_tooltip_shortcut("Duplicate the selected animation(s).", settings.shortcutDuplicate);
@@ -155,7 +150,7 @@ namespace anm2ed::animations
{ {
if (ImGui::Button("Merge", widgetSize)) if (ImGui::Button("Merge", widgetSize))
{ {
ImGui::OpenPopup("Merge Animations"); mergePopup.open();
mergeSelection.clear(); mergeSelection.clear();
mergeTarget = *selection.begin(); mergeTarget = *selection.begin();
} }
@@ -169,14 +164,15 @@ namespace anm2ed::animations
ImGui::SameLine(); ImGui::SameLine();
imgui::shortcut(settings.shortcutRemove, true); imgui::shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove", widgetSize)) if (ImGui::Button("Remove", widgetSize))
{ {
/* /*
auto selectionErase = set::to_size_t(selection); auto selectionErase = set::to_size_t(selection);
if (selectionErase.contains(document->reference.animationIndex)) document->reference.animationIndex = -1; if (selectionErase.contains(document.reference.animationIndex)) document.reference.animationIndex = -1;
vector::range_erase(anm2.animations.items, selectionErase); vector::range_erase(anm2.animations.items, selectionErase);
*/ */
document.change(change::ANIMATIONS);
selection.clear(); selection.clear();
} }
imgui::set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutDuplicate); imgui::set_item_tooltip_shortcut("Remove the selected animation(s).", settings.shortcutDuplicate);
@@ -185,7 +181,7 @@ namespace anm2ed::animations
ImGui::BeginDisabled(selection.size() != 1); ImGui::BeginDisabled(selection.size() != 1);
{ {
imgui::shortcut(settings.shortcutDefault, true); imgui::shortcut(settings.shortcutDefault);
if (ImGui::Button("Default", widgetSize)) if (ImGui::Button("Default", widgetSize))
anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name; anm2.animations.defaultAnimation = anm2.animations.items[*selection.begin()].name;
imgui::set_item_tooltip_shortcut("Set the selected animation as the default.", settings.shortcutDuplicate); imgui::set_item_tooltip_shortcut("Set the selected animation as the default.", settings.shortcutDuplicate);
@@ -194,27 +190,23 @@ namespace anm2ed::animations
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
auto viewport = ImGui::GetMainViewport(); mergePopup.trigger();
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal(mergePopup.label, &mergePopup.isOpen, ImGuiWindowFlags_NoResize))
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * 0.5f));
if (ImGui::BeginPopupModal("Merge Animations", nullptr, ImGuiWindowFlags_NoResize))
{ {
auto merge_close = [&]() auto merge_close = [&]()
{ {
mergeSelection.clear(); mergeSelection.clear();
ImGui::CloseCurrentPopup(); mergePopup.close();
}; };
auto& type = settings.mergeType; auto& type = settings.mergeType;
auto& isDeleteAnimationsAfter = settings.mergeIsDeleteAnimationsAfter; auto& isDeleteAnimationsAfter = settings.mergeIsDeleteAnimationsAfter;
mergeStorage.UserData = &mergeSelection; mergeStorage.user_data_set(&mergeSelection);
mergeStorage.AdapterSetItemSelected = imgui::external_storage_set;
auto footerSize = ImVec2(0, imgui::footer_height_get()); auto footerSize = imgui::footer_size_get();
auto optionsSize = imgui::child_size_get(2, true); auto optionsSize = imgui::child_size_get(2);
auto deleteAfterSize = imgui::child_size_get(); auto deleteAfterSize = imgui::child_size_get();
auto animationsSize = auto animationsSize =
ImVec2(0, ImGui::GetContentRegionAvail().y - ImVec2(0, ImGui::GetContentRegionAvail().y -
@@ -222,9 +214,7 @@ namespace anm2ed::animations
if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders)) if (ImGui::BeginChild("Animations", animationsSize, ImGuiChildFlags_Borders))
{ {
ImGuiMultiSelectIO* io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, mergeSelection.size(), mergeSelection.begin();
anm2.animations.items.size());
mergeStorage.ApplyRequests(io);
for (auto [i, animation] : std::views::enumerate(anm2.animations.items)) for (auto [i, animation] : std::views::enumerate(anm2.animations.items))
{ {
@@ -238,14 +228,12 @@ namespace anm2ed::animations
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); mergeSelection.end();
mergeStorage.ApplyRequests(io);
} }
ImGui::EndChild(); 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); 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))

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "document_manager.h" #include "document.h"
#include "imgui.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
@@ -8,13 +9,14 @@ namespace anm2ed::animations
{ {
class Animations class Animations
{ {
ImGuiSelectionExternalStorage mergeStorage{}; imgui::PopupHelper mergePopup{imgui::PopupHelper("Merge Animations")};
ImGuiSelectionExternalStorage storage{}; imgui::MultiSelectStorage mergeStorage{};
imgui::MultiSelectStorage storage{};
std::set<int> mergeSelection{}; std::set<int> mergeSelection{};
int mergeTarget{}; int mergeTarget{};
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document::Document& document, int& documentIndex, settings::Settings& settings,
resources::Resources& resources); resources::Resources& resources);
}; };
} }

View File

@@ -16,6 +16,7 @@ using namespace anm2ed::util;
namespace anm2ed::anm2 namespace anm2ed::anm2
{ {
void Reference::previous_frame(int max) void Reference::previous_frame(int max)
{ {
frameIndex = glm::clamp(--frameIndex, 0, max); frameIndex = glm::clamp(--frameIndex, 0, max);
@@ -213,20 +214,6 @@ namespace anm2ed::anm2
} }
} }
bool Content::spritesheet_add(const std::string& directory, const std::string& path, int& id)
{
Spritesheet spritesheet(directory, path);
if (!spritesheet.is_valid()) return false;
id = map::next_id_get(spritesheets);
spritesheets[id] = std::move(spritesheet);
return true;
}
void Content::spritesheet_remove(int& id)
{
spritesheets.erase(id);
}
std::set<int> Content::spritesheets_unused() std::set<int> Content::spritesheets_unused()
{ {
std::set<int> used; std::set<int> used;
@@ -778,7 +765,16 @@ namespace anm2ed::anm2
bool Anm2::spritesheet_add(const std::string& directory, const std::string& path, int& id) bool Anm2::spritesheet_add(const std::string& directory, const std::string& path, int& id)
{ {
return content.spritesheet_add(directory, path, id); Spritesheet spritesheet(directory, path);
if (!spritesheet.is_valid()) return false;
id = map::next_id_get(content.spritesheets);
content.spritesheets[id] = std::move(spritesheet);
return true;
}
void Anm2::spritesheet_remove(int id)
{
content.spritesheets.erase(id);
} }
Spritesheet* Anm2::spritesheet_get(int id) Spritesheet* Anm2::spritesheet_get(int id)
@@ -786,24 +782,60 @@ namespace anm2ed::anm2
return map::find(content.spritesheets, id); return map::find(content.spritesheets, id);
} }
void Anm2::spritesheet_remove(int id)
{
content.spritesheet_remove(id);
}
std::set<int> Anm2::spritesheets_unused() std::set<int> Anm2::spritesheets_unused()
{ {
return content.spritesheets_unused(); return content.spritesheets_unused();
} }
void Anm2::layer_add(int& id) Reference Anm2::layer_add(Reference reference, std::string name, int spritesheetID, locale::Type locale)
{ {
content.layer_add(id); auto id = reference.itemID == -1 ? map::next_id_get(content.layers) : reference.itemID;
auto& layer = content.layers[id];
layer.name = !name.empty() ? name : layer.name;
layer.spritesheetID = content.spritesheets.contains(spritesheetID) ? spritesheetID : 0;
auto add = [&](Animation* animation, int id)
{
animation->layerAnimations[id] = Item();
animation->layerOrder.push_back(id);
};
if (locale == locale::GLOBAL)
{
for (auto& animation : animations.items)
if (!animation.layerAnimations.contains(id)) add(&animation, id);
}
else if (locale == locale::LOCAL)
{
if (auto animation = animation_get(reference))
if (!animation->layerAnimations.contains(id)) add(animation, id);
}
return {reference.animationIndex, LAYER, id};
} }
void Anm2::null_add(int& id) Reference Anm2::null_add(Reference reference, std::string name, locale::Type locale)
{ {
content.null_add(id); auto id = reference.itemID == -1 ? map::next_id_get(content.nulls) : reference.itemID;
auto& null = content.nulls[id];
null.name = !name.empty() ? name : null.name;
auto add = [&](Animation* animation, int id) { animation->nullAnimations[id] = Item(); };
if (locale == locale::GLOBAL)
{
for (auto& animation : animations.items)
if (!animation.nullAnimations.contains(id)) add(&animation, id);
}
else if (locale == locale::LOCAL)
{
if (auto animation = animation_get(reference))
if (!animation->nullAnimations.contains(id)) add(animation, id);
}
return {reference.animationIndex, LAYER, id};
} }
void Anm2::event_add(int& id) void Anm2::event_add(int& id)
@@ -811,45 +843,68 @@ namespace anm2ed::anm2
content.event_add(id); content.event_add(id);
} }
std::set<int> Anm2::events_unused() std::set<int> Anm2::events_unused(Reference reference)
{ {
std::set<int> used; std::set<int> used{};
for (auto& animation : animations.items) std::set<int> unused{};
for (auto& frame : animation.triggers.frames)
used.insert(frame.eventID); if (auto animation = animation_get(reference); animation)
for (auto& frame : animation->triggers.frames)
used.insert(frame.eventID);
else
for (auto& animation : animations.items)
for (auto& frame : animation.triggers.frames)
used.insert(frame.eventID);
std::set<int> unused;
for (auto& id : content.events | std::views::keys) for (auto& id : content.events | std::views::keys)
if (!used.contains(id)) unused.insert(id); if (!used.contains(id)) unused.insert(id);
return unused; return unused;
} }
std::set<int> Anm2::layers_unused() std::set<int> Anm2::layers_unused(Reference reference)
{ {
std::set<int> used; std::set<int> used{};
for (auto& animation : animations.items) std::set<int> unused{};
for (auto& id : animation.layerAnimations | std::views::keys)
used.insert(id); if (auto animation = animation_get(reference); animation)
for (auto& id : animation->layerAnimations | std::views::keys)
used.insert(id);
else
for (auto& animation : animations.items)
for (auto& id : animation.layerAnimations | std::views::keys)
used.insert(id);
std::set<int> unused;
for (auto& id : content.layers | std::views::keys) for (auto& id : content.layers | std::views::keys)
if (!used.contains(id)) unused.insert(id); if (!used.contains(id)) unused.insert(id);
return unused; return unused;
} }
std::set<int> Anm2::nulls_unused() std::set<int> Anm2::nulls_unused(Reference reference)
{ {
std::set<int> used; std::set<int> used{};
for (auto& animation : animations.items) std::set<int> unused{};
for (auto& id : animation.nullAnimations | std::views::keys)
used.insert(id); if (auto animation = animation_get(reference); animation)
for (auto& id : animation->nullAnimations | std::views::keys)
used.insert(id);
else
for (auto& animation : animations.items)
for (auto& id : animation.nullAnimations | std::views::keys)
used.insert(id);
std::set<int> unused;
for (auto& id : content.nulls | std::views::keys) for (auto& id : content.nulls | std::views::keys)
if (!used.contains(id)) unused.insert(id); if (!used.contains(id)) unused.insert(id);
return unused; return unused;
} }
std::vector<std::string> Anm2::spritesheet_names_get()
{
std::vector<std::string> spritesheets{};
for (auto& [id, spritesheet] : content.spritesheets)
spritesheets.push_back(std::format("#{} {}", id, spritesheet.path.c_str()));
return spritesheets;
}
} }

View File

@@ -21,6 +21,10 @@ namespace anm2ed::anm2
constexpr auto MERGED_STRING = "(Merged)"; constexpr auto MERGED_STRING = "(Merged)";
constexpr auto LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
constexpr auto NULL_FORMAT = "#{} {}";
constexpr auto SPRITESHEET_FORMAT = "#%d %s";
enum Type enum Type
{ {
NONE, NONE,
@@ -44,6 +48,8 @@ namespace anm2ed::anm2
auto operator<=>(const Reference&) const = default; auto operator<=>(const Reference&) const = default;
}; };
constexpr anm2::Reference REFERENCE_DEFAULT = {-1, anm2::NONE, -1, -1, -1};
class Info class Info
{ {
public: public:
@@ -228,11 +234,15 @@ namespace anm2ed::anm2
Spritesheet* spritesheet_get(int id); Spritesheet* spritesheet_get(int id);
void spritesheet_remove(int id); void spritesheet_remove(int id);
std::set<int> spritesheets_unused(); std::set<int> spritesheets_unused();
void layer_add(int& id); int layer_add();
void null_add(int& id); Reference layer_add(Reference reference = REFERENCE_DEFAULT, std::string name = {}, int spritesheetID = 0,
types::locale::Type locale = types::locale::GLOBAL);
Reference null_add(Reference reference = REFERENCE_DEFAULT, std::string name = {},
types::locale::Type locale = types::locale::GLOBAL);
void event_add(int& id); void event_add(int& id);
std::set<int> events_unused(); std::set<int> events_unused(Reference reference = REFERENCE_DEFAULT);
std::set<int> layers_unused(); std::set<int> layers_unused(Reference reference = REFERENCE_DEFAULT);
std::set<int> nulls_unused(); std::set<int> nulls_unused(Reference reference = REFERENCE_DEFAULT);
std::vector<std::string> spritesheet_names_get();
}; };
} }

View File

@@ -33,17 +33,17 @@ namespace anm2ed::dockspace
{ {
if (ImGui::DockSpace(ImGui::GetID("##DockSpace"), ImVec2(), ImGuiDockNodeFlags_PassthruCentralNode)) if (ImGui::DockSpace(ImGui::GetID("##DockSpace"), ImVec2(), ImGuiDockNodeFlags_PassthruCentralNode))
{ {
if (manager.get()) if (auto document = manager.get(); document)
{ {
if (settings.windowIsAnimationPreview) animationPreview.update(manager, settings, resources, playback); if (settings.windowIsAnimationPreview) animationPreview.update(manager, settings, resources, playback);
if (settings.windowIsAnimations) animations.update(manager, settings, resources); if (settings.windowIsAnimations) animations.update(*document, manager.selected, settings, resources);
if (settings.windowIsEvents) events.update(manager, settings, resources); if (settings.windowIsEvents) events.update(manager, settings, resources);
if (settings.windowIsFrameProperties) frameProperties.update(manager, settings); if (settings.windowIsFrameProperties) frameProperties.update(manager, settings);
if (settings.windowIsLayers) layers.update(manager, settings, resources); if (settings.windowIsLayers) layers.update(*document, settings, resources);
if (settings.windowIsNulls) nulls.update(manager, settings, resources); if (settings.windowIsNulls) nulls.update(*document, manager.selected, settings, resources);
if (settings.windowIsOnionskin) onionskin.update(settings); if (settings.windowIsOnionskin) onionskin.update(settings);
if (settings.windowIsSpritesheetEditor) spritesheetEditor.update(manager, settings, resources); if (settings.windowIsSpritesheetEditor) spritesheetEditor.update(manager, settings, resources);
if (settings.windowIsSpritesheets) spritesheets.update(manager, settings, resources, dialog); if (settings.windowIsSpritesheets) spritesheets.update(*document, settings, resources, dialog);
if (settings.windowIsTimeline) timeline.update(manager, settings, resources, playback); if (settings.windowIsTimeline) timeline.update(manager, settings, resources, playback);
if (settings.windowIsTools) tools.update(settings, resources); if (settings.windowIsTools) tools.update(settings, resources);
} }

View File

@@ -5,13 +5,21 @@
using namespace anm2ed::anm2; using namespace anm2ed::anm2;
using namespace anm2ed::filesystem; using namespace anm2ed::filesystem;
using namespace anm2ed::types;
namespace anm2ed::document namespace anm2ed::document
{ {
Document::Document() = default; Document::Document()
{
for (auto& value : isJustChanged)
value = true;
}
Document::Document(const std::string& path, bool isNew, std::string* errorString) Document::Document(const std::string& path, bool isNew, std::string* errorString)
{ {
for (auto& value : isJustChanged)
value = true;
if (!path_is_exist(path)) return; if (!path_is_exist(path)) return;
if (isNew) if (isNew)
@@ -23,7 +31,8 @@ namespace anm2ed::document
} }
this->path = path; this->path = path;
hash_set(); on_change();
clean();
} }
bool Document::save(const std::string& path, std::string* errorString) bool Document::save(const std::string& path, std::string* errorString)
@@ -32,7 +41,7 @@ namespace anm2ed::document
if (anm2.serialize(this->path, errorString)) if (anm2.serialize(this->path, errorString))
{ {
hash_set(); clean();
return true; return true;
} }
@@ -40,18 +49,20 @@ namespace anm2ed::document
} }
void Document::hash_set() void Document::hash_set()
{
hash = anm2.hash();
}
void Document::clean()
{ {
saveHash = anm2.hash(); saveHash = anm2.hash();
hash = saveHash; hash = saveHash;
} }
void Document::hash_time(double time, double interval) void Document::change(change::Type type)
{ {
if (time - lastHashTime > interval) hash_set();
{ isJustChanged[type] = true;
hash = anm2.hash();
lastHashTime = time;
}
} }
bool Document::is_dirty() bool Document::is_dirty()
@@ -59,6 +70,11 @@ namespace anm2ed::document
return hash != saveHash; return hash != saveHash;
} }
bool Document::is_just_changed(types::change::Type type)
{
return isJustChanged[type];
}
std::string Document::directory_get() std::string Document::directory_get()
{ {
return path.parent_path(); return path.parent_path();
@@ -79,6 +95,11 @@ namespace anm2ed::document
return anm2.frame_get(reference); return anm2.frame_get(reference);
} }
anm2::Item* Document::item_get()
{
return anm2.item_get(reference);
}
anm2::Spritesheet* Document::spritesheet_get() anm2::Spritesheet* Document::spritesheet_get()
{ {
return anm2.spritesheet_get(referenceSpritesheet); return anm2.spritesheet_get(referenceSpritesheet);
@@ -88,4 +109,23 @@ namespace anm2ed::document
{ {
return !path.empty(); return !path.empty();
} }
void Document::on_change()
{
if (is_just_changed(change::SPRITESHEETS))
{
spritesheetNames = anm2.spritesheet_names_get();
spritesheetNamesCstr.clear();
for (auto& name : spritesheetNames)
spritesheetNamesCstr.push_back(name.c_str());
}
}
void Document::update()
{
on_change();
for (auto& value : isJustChanged)
value = false;
}
}; };

View File

@@ -1,9 +1,11 @@
#pragma once #pragma once
#include "anm2.h"
#include <filesystem> #include <filesystem>
#include <set> #include <set>
#include "anm2.h"
#include "types.h"
#include <glm/glm.hpp> #include <glm/glm.hpp>
namespace anm2ed::document namespace anm2ed::document
@@ -22,6 +24,7 @@ namespace anm2ed::document
int overlayIndex{}; int overlayIndex{};
int referenceSpritesheet{-1}; int referenceSpritesheet{-1};
int referenceLayer{-1};
std::set<int> selectedEvents{}; std::set<int> selectedEvents{};
std::set<int> selectedLayers{}; std::set<int> selectedLayers{};
@@ -29,9 +32,12 @@ namespace anm2ed::document
std::set<int> selectedAnimations{}; std::set<int> selectedAnimations{};
std::set<int> selectedSpritesheets{}; std::set<int> selectedSpritesheets{};
std::vector<std::string> spritesheetNames{};
std::vector<const char*> spritesheetNamesCstr{};
uint64_t hash{}; uint64_t hash{};
uint64_t saveHash{}; uint64_t saveHash{};
double lastHashTime{}; bool isJustChanged[types::change::COUNT]{};
bool isOpen{true}; bool isOpen{true};
Document(); Document();
@@ -39,12 +45,17 @@ namespace anm2ed::document
Document(const std::string& path, bool isNew = false, std::string* errorString = nullptr); Document(const std::string& path, bool isNew = false, std::string* errorString = nullptr);
bool save(const std::string& path = {}, std::string* errorString = nullptr); bool save(const std::string& path = {}, std::string* errorString = nullptr);
void hash_set(); void hash_set();
void hash_time(double time, double interval = 1.0); void clean();
void on_change();
void change(types::change::Type type);
bool is_just_changed(types::change::Type type);
bool is_dirty(); bool is_dirty();
void update();
std::string directory_get(); std::string directory_get();
std::string filename_get(); std::string filename_get();
anm2::Animation* animation_get(); anm2::Animation* animation_get();
anm2::Frame* frame_get(); anm2::Frame* frame_get();
anm2::Item* item_get();
anm2::Spritesheet* spritesheet_get(); anm2::Spritesheet* spritesheet_get();
bool is_valid(); bool is_valid();
}; };

View File

@@ -1,9 +1,7 @@
#include "document_manager.h" #include "document_manager.h"
#include "toast.h"
#include "util.h" #include "util.h"
using namespace anm2ed::toast;
using namespace anm2ed::util; using namespace anm2ed::util;
namespace anm2ed::document_manager namespace anm2ed::document_manager
@@ -27,10 +25,8 @@ namespace anm2ed::document_manager
documents.emplace_back(std::move(document)); documents.emplace_back(std::move(document));
selected = documents.size() - 1; selected = documents.size() - 1;
pendingSelected = selected; pendingSelected = selected;
toasts.add(std::format("Opened document: {}", path));
return true; return true;
} }
toasts.add_error(std::format("Failed to open document: {} ({})", path, errorString));
return false; return false;
} }
@@ -47,10 +43,7 @@ namespace anm2ed::document_manager
document->path = !path.empty() ? path : document->path.string(); document->path = !path.empty() ? path : document->path.string();
if (document->save(document->path, &errorString)) document->save(document->path, &errorString);
toasts.add(std::format("Saved document: {}", document->path.string()));
else
toasts.add_error(std::format("Failed to save document: {} ({})", document->path.string(), errorString));
} }
void DocumentManager::save(const std::string& path) void DocumentManager::save(const std::string& path)
@@ -62,15 +55,4 @@ namespace anm2ed::document_manager
{ {
documents.erase(documents.begin() + index); documents.erase(documents.begin() + index);
} }
void DocumentManager::spritesheet_add(const std::string& path)
{
auto document = get();
if (!document) return;
if (int id{}; document->anm2.spritesheet_add(document->directory_get(), path, id))
toasts.add(std::format("Added spritesheet #{}: {}", id, path));
else
toasts.add(std::format("Failed to add spritesheet #{}: {}", id, path));
}
} }

View File

@@ -32,10 +32,8 @@ namespace anm2ed::documents
for (auto [i, document] : std::views::enumerate(manager.documents)) for (auto [i, document] : std::views::enumerate(manager.documents))
{ {
auto isDirty = document.is_dirty(); auto isDirty = document.is_dirty();
auto isRequested = i == manager.pendingSelected;
auto isSelected = i == manager.selected; auto isSelected = i == manager.selected;
auto isRequested = i == manager.pendingSelected;
if (isSelected) document.hash_time(ImGui::GetTime());
auto font = isDirty ? font::ITALICS : font::REGULAR; auto font = isDirty ? font::ITALICS : font::REGULAR;
@@ -67,23 +65,18 @@ namespace anm2ed::documents
else else
manager.close(i); manager.close(i);
} }
if (isSelected) document.update();
} }
ImGui::EndTabBar(); ImGui::EndTabBar();
} }
if (isOpenCloseDocumentPopup) closePopup.trigger();
{
ImGui::OpenPopup("Close Document");
isOpenCloseDocumentPopup = false;
}
if (isCloseDocument) if (isCloseDocument)
{ {
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal(closePopup.label, &closePopup.isOpen, ImGuiWindowFlags_NoResize))
ImGui::SetNextWindowSize(ImVec2(), ImGuiCond_None);
if (ImGui::BeginPopupModal("Close Document", nullptr, ImGuiWindowFlags_NoResize))
{ {
auto closeDocument = manager.get(closeDocumentIndex); auto closeDocument = manager.get(closeDocumentIndex);
@@ -96,8 +89,7 @@ namespace anm2ed::documents
auto close = [&]() auto close = [&]()
{ {
closeDocumentIndex = 0; closeDocumentIndex = 0;
isCloseDocument = false; closePopup.close();
ImGui::CloseCurrentPopup();
}; };
if (ImGui::Button("Yes", widgetSize)) if (ImGui::Button("Yes", widgetSize))

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "document_manager.h" #include "document_manager.h"
#include "imgui.h"
#include "resources.h" #include "resources.h"
#include "taskbar.h" #include "taskbar.h"
@@ -11,6 +12,7 @@ namespace anm2ed::documents
bool isCloseDocument{}; bool isCloseDocument{};
bool isOpenCloseDocumentPopup{}; bool isOpenCloseDocumentPopup{};
int closeDocumentIndex{}; int closeDocumentIndex{};
imgui::PopupHelper closePopup{imgui::PopupHelper("Close Document", imgui::POPUP_TO_CONTENT)};
public: public:
float height{}; float height{};

View File

@@ -2,8 +2,6 @@
#include <ranges> #include <ranges>
#include "imgui.h"
using namespace anm2ed::document_manager; using namespace anm2ed::document_manager;
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::resources; using namespace anm2ed::resources;
@@ -13,22 +11,22 @@ namespace anm2ed::events
{ {
void Events::update(DocumentManager& manager, Settings& settings, Resources& resources) void Events::update(DocumentManager& manager, Settings& settings, Resources& resources)
{ {
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& selection = document.selectedEvents;
if (document.is_just_changed(change::EVENTS)) unusedEventIDs = anm2.events_unused();
storage.user_data_set(&selection);
if (ImGui::Begin("Events", &settings.windowIsEvents)) if (ImGui::Begin("Events", &settings.windowIsEvents))
{ {
auto document = manager.get(); auto childSize = imgui::size_without_footer_get();
anm2::Anm2& anm2 = document->anm2; bool isRenamed{};
auto& selection = document->selectedEvents;
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
auto childSize = imgui::size_with_footer_get();
if (ImGui::BeginChild("##Events Child", childSize, true)) if (ImGui::BeginChild("##Events Child", childSize, true))
{ {
ImGuiMultiSelectIO* io = storage.begin(anm2.content.events.size());
ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), anm2.content.events.size());
storage.ApplyRequests(io);
for (auto& [id, event] : anm2.content.events) for (auto& [id, event] : anm2.content.events)
{ {
@@ -36,8 +34,9 @@ namespace anm2ed::events
ImGui::PushID(id); ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id); ImGui::SetNextItemSelectionUserData(id);
imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id), if (imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
event.name, isSelected); event.name, isSelected, 0, &isRenamed))
if (isRenamed) document.change(change::EVENTS);
if (ImGui::BeginItemTooltip()) if (ImGui::BeginItemTooltip())
{ {
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
@@ -48,31 +47,33 @@ namespace anm2ed::events
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); storage.end();
storage.ApplyRequests(io);
} }
ImGui::EndChild(); ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2); auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize)) if (ImGui::Button("Add", widgetSize))
{ {
int id{}; int id{};
anm2.event_add(id); anm2.event_add(id);
selection = {id}; selection = {id};
document.change(change::EVENTS);
} }
imgui::set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
ImGui::SameLine(); ImGui::SameLine();
std::set<int> unusedEventIDs = anm2.events_unused(); imgui::shortcut(settings.shortcutRemove);
imgui::shortcut(settings.shortcutRemove, true);
ImGui::BeginDisabled(unusedEventIDs.empty()); ImGui::BeginDisabled(unusedEventIDs.empty());
{ {
if (ImGui::Button("Remove Unused", widgetSize)) if (ImGui::Button("Remove Unused", widgetSize))
{
for (auto& id : unusedEventIDs) for (auto& id : unusedEventIDs)
anm2.content.layers.erase(id); anm2.content.events.erase(id);
document.change(change::EVENTS);
unusedEventIDs.clear();
}
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)", imgui::set_item_tooltip_shortcut("Remove unused events (i.e., ones not used by any trigger in any animation.)",

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "document_manager.h" #include "document_manager.h"
#include "imgui.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
@@ -8,7 +9,8 @@ namespace anm2ed::events
{ {
class Events class Events
{ {
ImGuiSelectionExternalStorage storage{}; imgui::MultiSelectStorage storage{};
std::set<int> unusedEventIDs{};
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document_manager::DocumentManager& manager, settings::Settings& settings,

View File

@@ -100,7 +100,9 @@ namespace anm2ed::frame_properties
"%s", "Toggle the frame interpolating; i.e., blending its values into the next frame based on the time."); "%s", "Toggle the frame interpolating; i.e., blending its values into the next frame based on the time.");
ImGui::SameLine(); ImGui::SameLine();
ImGui::EndDisabled();
ImGui::Checkbox("Round", &settings.propertiesIsRound); ImGui::Checkbox("Round", &settings.propertiesIsRound);
ImGui::BeginDisabled(!frame);
ImGui::SetItemTooltip( ImGui::SetItemTooltip(
"%s", "When toggled, decimal values will be snapped to their nearest whole value when changed."); "%s", "When toggled, decimal values will be snapped to their nearest whole value when changed.");

View File

@@ -6,8 +6,12 @@
#include <sstream> #include <sstream>
#include <unordered_map> #include <unordered_map>
using namespace anm2ed::types;
using namespace glm;
namespace anm2ed::imgui namespace anm2ed::imgui
{ {
std::string chord_to_string(ImGuiKeyChord chord) std::string chord_to_string(ImGuiKeyChord chord)
{ {
std::string result; std::string result;
@@ -71,14 +75,19 @@ namespace anm2ed::imgui
ImGui::GetStyle().ItemSpacing.y * (itemCount); ImGui::GetStyle().ItemSpacing.y * (itemCount);
} }
ImVec2 size_with_footer_get(int rowCount) ImVec2 footer_size_get(int itemCount)
{
return ImVec2(ImGui::GetContentRegionAvail().x, footer_height_get(itemCount));
}
ImVec2 size_without_footer_get(int rowCount)
{ {
return ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - footer_height_get(rowCount)); return ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - footer_height_get(rowCount));
} }
ImVec2 child_size_get(int rowCount, bool isContentRegionAvail) ImVec2 child_size_get(int rowCount)
{ {
return ImVec2(isContentRegionAvail ? ImGui::GetContentRegionAvail().x : 0, return ImVec2(ImGui::GetContentRegionAvail().x,
(ImGui::GetFrameHeightWithSpacing() * rowCount) + (ImGui::GetStyle().WindowPadding.y * 2.0f)); (ImGui::GetFrameHeightWithSpacing() * rowCount) + (ImGui::GetStyle().WindowPadding.y * 2.0f));
} }
@@ -107,8 +116,13 @@ namespace anm2ed::imgui
ImGui::Combo(label.c_str(), index, items.data(), (int)items.size()); ImGui::Combo(label.c_str(), index, items.data(), (int)items.size());
} }
void combo_strings(const std::string& label, int* index, std::vector<const char*>& strings)
{
ImGui::Combo(label.c_str(), index, strings.data(), (int)strings.size());
}
bool selectable_input_text(const std::string& label, const std::string& id, std::string& text, bool isSelected, bool selectable_input_text(const std::string& label, const std::string& id, std::string& text, bool isSelected,
ImGuiSelectableFlags flags) ImGuiSelectableFlags flags, bool* isRenamed)
{ {
static std::string editID{}; static std::string editID{};
static bool isJustEdit{}; static bool isJustEdit{};
@@ -128,6 +142,7 @@ namespace anm2ed::imgui
{ {
editID.clear(); editID.clear();
isActivated = true; isActivated = true;
if (isRenamed) *isRenamed = true;
} }
if (ImGui::IsItemDeactivatedAfterEdit() || ImGui::IsKeyPressed(ImGuiKey_Escape)) editID.clear(); if (ImGui::IsItemDeactivatedAfterEdit() || ImGui::IsKeyPressed(ImGuiKey_Escape)) editID.clear();
} }
@@ -146,10 +161,9 @@ namespace anm2ed::imgui
return isActivated; return isActivated;
} }
void set_item_tooltip_shortcut(const std::string& tooltip, const std::string& shortcut) void set_item_tooltip_shortcut(const char* tooltip, const std::string& shortcut)
{ {
auto text = shortcut.empty() ? tooltip : std::format("{}\n(Shortcut: {})", tooltip, shortcut); ImGui::SetItemTooltip("%s\n(Shortcut: %s)", tooltip, shortcut.c_str());
ImGui::SetItemTooltip("%s", text.c_str());
} }
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected) void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected)
@@ -219,17 +233,71 @@ namespace anm2ed::imgui
return false; return false;
} }
bool shortcut(std::string string, bool isSet, bool isGlobal, bool isPopupBlock) bool shortcut(std::string string, shortcut::Type type)
{ {
if (isPopupBlock && ImGui::GetTopMostPopupModal() != nullptr) return false; if (ImGui::GetTopMostPopupModal() != nullptr) return false;
auto flags = isGlobal ? ImGuiInputFlags_RouteGlobal : ImGuiInputFlags_RouteFocused; auto flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
: ImGuiInputFlags_RouteFocused;
if (isSet) if (type == shortcut::GLOBAL_SET || type == shortcut::FOCUSED_SET)
{ {
ImGui::SetNextItemShortcut(string_to_chord(string), flags); ImGui::SetNextItemShortcut(string_to_chord(string), flags);
return true; return false;
} }
return ImGui::Shortcut(string_to_chord(string), flags); return ImGui::Shortcut(string_to_chord(string), flags);
} }
MultiSelectStorage::MultiSelectStorage()
{
internal.AdapterSetItemSelected = external_storage_set;
}
void MultiSelectStorage::user_data_set(std::set<int>* userData)
{
internal.UserData = userData;
this->userData = userData;
}
void MultiSelectStorage::begin(size_t size)
{
auto io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, userData ? userData->size() : 0, size);
internal.ApplyRequests(io);
}
void MultiSelectStorage::end()
{
auto io = ImGui::EndMultiSelect();
internal.ApplyRequests(io);
}
PopupHelper::PopupHelper(const char* label, float percent, bool isNoHeight)
{
this->label = label;
this->percent = percent;
this->isNoHeight = isNoHeight;
}
void PopupHelper::open()
{
isOpen = true;
isTriggered = true;
}
void PopupHelper::trigger()
{
if (isTriggered) ImGui::OpenPopup(label);
isTriggered = false;
auto viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, to_imvec2(vec2(0.5f)));
if (isNoHeight)
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x * percent, 0));
else
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * percent));
}
void PopupHelper::close()
{
isOpen = false;
}
} }

View File

@@ -1,12 +1,19 @@
#pragma once #pragma once
#include <imgui/imgui.h> #include <imgui/imgui.h>
#include <set>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "types.h"
namespace anm2ed::imgui namespace anm2ed::imgui
{ {
constexpr auto POPUP_TO_CONTENT = 0.0f;
constexpr auto POPUP_SMALL = 0.25f;
constexpr auto POPUP_NORMAL = 0.5f;
const std::unordered_map<std::string, ImGuiKey> KEY_MAP = {{"A", ImGuiKey_A}, const std::unordered_map<std::string, ImGuiKey> KEY_MAP = {{"A", ImGuiKey_A},
{"B", ImGuiKey_B}, {"B", ImGuiKey_B},
{"C", ImGuiKey_C}, {"C", ImGuiKey_C},
@@ -122,17 +129,46 @@ namespace anm2ed::imgui
float row_widget_width_get(int count, float width = ImGui::GetContentRegionAvail().x); float row_widget_width_get(int count, float width = ImGui::GetContentRegionAvail().x);
ImVec2 widget_size_with_row_get(int count, float width = ImGui::GetContentRegionAvail().x); ImVec2 widget_size_with_row_get(int count, float width = ImGui::GetContentRegionAvail().x);
float footer_height_get(int itemCount = 1); float footer_height_get(int itemCount = 1);
ImVec2 size_with_footer_get(int rowCount = 1); ImVec2 footer_size_get(int itemCount = 1);
ImVec2 child_size_get(int rowCount = 1, bool isContentRegionAvail = false); ImVec2 size_without_footer_get(int rowCount = 1);
ImVec2 child_size_get(int rowCount = 1);
int input_text_callback(ImGuiInputTextCallbackData* data); int input_text_callback(ImGuiInputTextCallbackData* data);
bool input_text_string(const char* label, std::string* string, ImGuiInputTextFlags flags = 0); bool input_text_string(const char* label, std::string* string, ImGuiInputTextFlags flags = 0);
void combo_strings(const std::string& label, int* index, std::vector<std::string>& strings); void combo_strings(const std::string& label, int* index, std::vector<std::string>& strings);
void combo_strings(const std::string& label, int* index, std::vector<const char*>& strings);
bool selectable_input_text(const std::string& label, const std::string& id, std::string& text, bool selectable_input_text(const std::string& label, const std::string& id, std::string& text,
bool isSelected = false, ImGuiSelectableFlags flags = 0); bool isSelected = false, ImGuiSelectableFlags flags = 0, bool* isRenamed = nullptr);
void set_item_tooltip_shortcut(const std::string& tooltip, const std::string& shortcut = {}); void set_item_tooltip_shortcut(const char* tooltip, const std::string& shortcut = {});
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected); void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected);
ImVec2 icon_size_get(); ImVec2 icon_size_get();
bool chord_held(ImGuiKeyChord chord); bool chord_held(ImGuiKeyChord chord);
bool chord_repeating(ImGuiKeyChord chord, float delay = 0.125f, float rate = 0.025f); bool chord_repeating(ImGuiKeyChord chord, float delay = 0.125f, float rate = 0.025f);
bool shortcut(std::string string, bool isSet = false, bool isGlobal = false, bool isPopupBlock = true); bool shortcut(std::string string, types::shortcut::Type type = types::shortcut::FOCUSED_SET);
class MultiSelectStorage
{
public:
ImGuiSelectionExternalStorage internal{};
std::set<int>* userData{};
MultiSelectStorage();
void user_data_set(std::set<int>* userData);
void begin(size_t size);
void end();
};
class PopupHelper
{
public:
const char* label{};
bool isOpen{};
bool isTriggered{};
bool isNoHeight{};
float percent{};
PopupHelper(const char* label, float percent = POPUP_NORMAL, bool isNoHeight = false);
void open();
void trigger();
void close();
};
} }

View File

@@ -2,80 +2,151 @@
#include <ranges> #include <ranges>
#include "imgui.h" #include "util.h"
using namespace anm2ed::document_manager; using namespace anm2ed::document;
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::resources; using namespace anm2ed::resources;
using namespace anm2ed::types; using namespace anm2ed::types;
using namespace anm2ed::util;
namespace anm2ed::layers namespace anm2ed::layers
{ {
void Layers::update(DocumentManager& manager, Settings& settings, Resources& resources) void Layers::update(Document& document, Settings& settings, Resources& resources)
{ {
auto& anm2 = document.anm2;
auto& selection = document.selectedLayers;
auto& referenceLayer = document.referenceLayer;
if (document.is_just_changed(change::LAYERS)) unusedLayerIDs = anm2.layers_unused();
storage.user_data_set(&selection);
auto properties_popup_open = [&](int id = -1)
{
if (id == -1)
{
isAdd = true;
editLayer = anm2::Layer();
}
else
editLayer = anm2.content.layers.at(id);
propertiesPopup.open();
};
if (ImGui::Begin("Layers", &settings.windowIsLayers)) if (ImGui::Begin("Layers", &settings.windowIsLayers))
{ {
auto document = manager.get(); auto childSize = imgui::size_without_footer_get();
anm2::Anm2& anm2 = document->anm2;
auto& selection = document->selectedLayers;
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
auto childSize = imgui::size_with_footer_get();
if (ImGui::BeginChild("##Layers Child", childSize, true)) if (ImGui::BeginChild("##Layers Child", childSize, true))
{ {
ImGuiMultiSelectIO* io = storage.begin(anm2.content.layers.size());
ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), anm2.content.layers.size());
storage.ApplyRequests(io);
for (auto& [id, layer] : anm2.content.layers) for (auto& [id, layer] : anm2.content.layers)
{ {
auto isSelected = selection.contains(id); auto isSelected = selection.contains(id);
auto isReferenced = referenceLayer == id;
ImGui::PushID(id); ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id); ImGui::SetNextItemSelectionUserData(id);
imgui::selectable_input_text(std::format("#{} {}", id, layer.name), if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
std::format("###Document #{} Layer #{}", manager.selected, id), layer.name, ImGui::Selectable(std::format("#{} {} (Spritesheet: #{})", id, layer.name, layer.spritesheetID).c_str(),
isSelected); isSelected);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
referenceLayer = id;
properties_popup_open(id);
}
if (isReferenced) ImGui::PopFont();
if (ImGui::BeginItemTooltip()) if (ImGui::BeginItemTooltip())
{ {
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(layer.name.c_str()); ImGui::TextUnformatted(layer.name.c_str());
ImGui::TextUnformatted(std::format("ID: {}", id).c_str());
ImGui::TextUnformatted(std::format("Spritesheet ID: {}", layer.spritesheetID).c_str());
ImGui::PopFont(); ImGui::PopFont();
ImGui::Text("ID: %d", id);
ImGui::Text("Spritesheet ID: %d", layer.spritesheetID);
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); storage.end();
storage.ApplyRequests(io);
} }
ImGui::EndChild(); ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2); auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
ImGui::Button("Add", widgetSize); if (ImGui::Button("Add", widgetSize)) properties_popup_open();
imgui::set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd);
ImGui::SameLine(); ImGui::SameLine();
std::set<int> unusedLayersIDs = anm2.layers_unused(); imgui::shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unusedLayerIDs.empty());
imgui::shortcut(settings.shortcutRemove, true);
ImGui::BeginDisabled(unusedLayersIDs.empty());
{ {
if (ImGui::Button("Remove Unused", widgetSize)) if (ImGui::Button("Remove Unused", widgetSize))
for (auto& id : unusedLayersIDs) {
for (auto& id : unusedLayerIDs)
anm2.content.layers.erase(id); anm2.content.layers.erase(id);
document.change(change::LAYERS);
unusedLayerIDs.clear();
}
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)", imgui::set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
settings.shortcutRemove); settings.shortcutRemove);
} }
ImGui::End(); ImGui::End();
propertiesPopup.trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto childSize = imgui::child_size_get(2);
auto& layer = editLayer;
auto close = [&]()
{
isAdd = false;
editLayer = anm2::Layer();
propertiesPopup.close();
};
if (ImGui::BeginChild("Child", childSize, ImGuiChildFlags_Borders))
{
imgui::input_text_string("Name", &layer.name);
ImGui::SetItemTooltip("Set the item's name.");
imgui::combo_strings("Spritesheet", &layer.spritesheetID, document.spritesheetNames);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
}
ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2);
if (ImGui::Button(isAdd ? "Add" : "Confirm", widgetSize))
{
if (isAdd)
{
auto id = map::next_id_get(anm2.content.layers);
anm2.content.layers[id] = editLayer;
referenceLayer = id;
}
else
anm2.content.layers[referenceLayer] = editLayer;
document.change(change::LAYERS);
close();
}
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) close();
ImGui::EndPopup();
}
referenceLayer = propertiesPopup.isOpen ? referenceLayer : -1;
} }
} }

View File

@@ -1,17 +1,22 @@
#pragma once #pragma once
#include "document_manager.h" #include "document.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
#include "imgui.h"
namespace anm2ed::layers namespace anm2ed::layers
{ {
class Layers class Layers
{ {
ImGuiSelectionExternalStorage storage{}; bool isAdd{};
imgui::PopupHelper propertiesPopup{imgui::PopupHelper("Layer Properties", imgui::POPUP_SMALL, true)};
imgui::MultiSelectStorage storage;
anm2::Layer editLayer{};
std::set<int> unusedLayerIDs{};
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document::Document& document, settings::Settings& settings, resources::Resources& resources);
resources::Resources& resources);
}; };
} }

View File

@@ -2,74 +2,68 @@
#include <ranges> #include <ranges>
#include "imgui.h" using namespace anm2ed::document;
using namespace anm2ed::document_manager;
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::resources; using namespace anm2ed::resources;
using namespace anm2ed::types; using namespace anm2ed::types;
namespace anm2ed::nulls namespace anm2ed::nulls
{ {
void Nulls::update(DocumentManager& manager, Settings& settings, Resources& resources) void Nulls::update(Document& document, int& documentIndex, Settings& settings, Resources& resources)
{ {
auto& anm2 = document.anm2;
auto& selection = document.selectedNulls;
if (document.is_just_changed(change::NULLS)) unusedNullsIDs = anm2.nulls_unused();
storage.user_data_set(&selection);
if (ImGui::Begin("Nulls", &settings.windowIsNulls)) if (ImGui::Begin("Nulls", &settings.windowIsNulls))
{ {
auto document = manager.get(); auto childSize = imgui::size_without_footer_get();
anm2::Anm2& anm2 = document->anm2;
auto& selection = document->selectedNulls;
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
auto childSize = imgui::size_with_footer_get();
if (ImGui::BeginChild("##Nulls Child", childSize, true)) if (ImGui::BeginChild("##Nulls Child", childSize, true))
{ {
ImGuiMultiSelectIO* io = storage.begin(anm2.content.nulls.size());
ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), anm2.content.nulls.size());
storage.ApplyRequests(io);
for (auto& [id, null] : anm2.content.nulls) for (auto& [id, null] : anm2.content.nulls)
{ {
const bool isSelected = selection.contains(id); auto isSelected = selection.contains(id);
ImGui::PushID(id); ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id); ImGui::SetNextItemSelectionUserData(id);
imgui::selectable_input_text(std::format("#{} {}", id, null.name), imgui::selectable_input_text(std::format("#{} {}", id, null.name),
std::format("###Document #{} Null #{}", manager.selected, id), null.name, std::format("###Document #{} Null #{}", documentIndex, id), null.name,
isSelected); isSelected);
if (ImGui::BeginItemTooltip()) if (ImGui::BeginItemTooltip())
{ {
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(null.name.c_str()); ImGui::TextUnformatted(null.name.c_str());
ImGui::TextUnformatted(std::format("ID: {}", id).c_str());
ImGui::PopFont(); ImGui::PopFont();
ImGui::Text("ID: %d", id);
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); storage.end();
storage.ApplyRequests(io);
} }
ImGui::EndChild(); ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2); auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
ImGui::Button("Add", widgetSize); ImGui::Button("Add", widgetSize);
imgui::set_item_tooltip_shortcut("Add a null.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Add a null.", settings.shortcutAdd);
ImGui::SameLine(); ImGui::SameLine();
std::set<int> unusedNullsIDs = anm2.nulls_unused(); imgui::shortcut(settings.shortcutRemove);
imgui::shortcut(settings.shortcutRemove, true);
ImGui::BeginDisabled(unusedNullsIDs.empty()); ImGui::BeginDisabled(unusedNullsIDs.empty());
{ {
if (ImGui::Button("Remove Unused", widgetSize)) if (ImGui::Button("Remove Unused", widgetSize))
for (auto& id : unusedNullsIDs) for (auto& id : unusedNullsIDs)
anm2.content.nulls.erase(id); anm2.content.nulls.erase(id);
document.change(change::NULLS);
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)", imgui::set_item_tooltip_shortcut("Remove unused nulls (i.e., ones not used in any animation.)",

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "document_manager.h" #include "document.h"
#include "imgui.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
@@ -8,10 +9,11 @@ namespace anm2ed::nulls
{ {
class Nulls class Nulls
{ {
ImGuiSelectionExternalStorage storage{}; imgui::MultiSelectStorage storage{};
std::set<int> unusedNullsIDs{};
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document::Document& document, int& documentIndex, settings::Settings& settings,
resources::Resources& resources); resources::Resources& resources);
}; };
} }

View File

@@ -5,6 +5,7 @@
#include "imgui.h" #include "imgui.h"
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::types;
using namespace glm; using namespace glm;
namespace anm2ed::onionskin namespace anm2ed::onionskin
@@ -23,7 +24,7 @@ namespace anm2ed::onionskin
ImGui::PopID(); ImGui::PopID();
}; };
imgui::shortcut(settings.shortcutOnionskin, true, true); imgui::shortcut(settings.shortcutOnionskin);
ImGui::Checkbox("Enabled", &settings.onionskinIsEnabled); ImGui::Checkbox("Enabled", &settings.onionskinIsEnabled);
order_configure("Before", settings.onionskinBeforeCount, settings.onionskinBeforeColor); order_configure("Before", settings.onionskinBeforeCount, settings.onionskinBeforeColor);
@@ -36,7 +37,8 @@ namespace anm2ed::onionskin
ImGui::RadioButton("After", &settings.onionskinDrawOrder, ABOVE); ImGui::RadioButton("After", &settings.onionskinDrawOrder, ABOVE);
} }
if (imgui::shortcut(settings.shortcutOnionskin)) settings.onionskinIsEnabled = !settings.onionskinIsEnabled; if (imgui::shortcut(settings.shortcutOnionskin), shortcut::GLOBAL)
settings.onionskinIsEnabled = !settings.onionskinIsEnabled;
ImGui::End(); ImGui::End();
} }

View File

@@ -120,6 +120,8 @@ namespace anm2ed::settings
X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, "Round Rotation", BOOL, true) \ X(BAKE_IS_ROUND_ROTATION, bakeIsRoundRotation, "Round Rotation", BOOL, true) \
\ \
X(TIMELINE_ADD_ITEM_TYPE, timelineAddItemType, "Add Item Type", INT, anm2::LAYER) \ 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_SHOW_UNUSED, timelineIsShowUnused, "##Show Unused", BOOL, true) \
\ \
X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, "Enabled", BOOL, false) \ X(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, "Enabled", BOOL, false) \

View File

@@ -1,35 +1,33 @@
#include "spritesheets.h" #include "spritesheets.h"
#include <ranges>
#include "imgui.h" #include "imgui.h"
#include "toast.h" #include "toast.h"
#include "types.h" #include <ranges>
using namespace anm2ed::anm2; using namespace anm2ed::anm2;
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::resources; using namespace anm2ed::resources;
using namespace anm2ed::dialog; using namespace anm2ed::dialog;
using namespace anm2ed::document_manager; using namespace anm2ed::document;
using namespace anm2ed::types; using namespace anm2ed::types;
using namespace anm2ed::toast; using namespace anm2ed::toast;
using namespace glm; using namespace glm;
namespace anm2ed::spritesheets namespace anm2ed::spritesheets
{ {
void Spritesheets::update(DocumentManager& manager, Settings& settings, Resources& resources, Dialog& dialog) void Spritesheets::update(Document& document, Settings& settings, Resources& resources, Dialog& dialog)
{ {
auto& anm2 = document.anm2;
auto& selection = document.selectedSpritesheets;
if (document.is_just_changed(change::SPRITESHEETS)) unusedSpritesheetIDs = anm2.spritesheets_unused();
if (ImGui::Begin("Spritesheets", &settings.windowIsSpritesheets)) if (ImGui::Begin("Spritesheets", &settings.windowIsSpritesheets))
{ {
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& selection = document.selectedSpritesheets;
auto style = ImGui::GetStyle(); auto style = ImGui::GetStyle();
static ImGuiSelectionExternalStorage storage{}; storage.user_data_set(&selection);
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
auto childSize = imgui::size_with_footer_get(2); auto childSize = imgui::size_without_footer_get(2);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2()); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
@@ -37,12 +35,10 @@ namespace anm2ed::spritesheets
{ {
auto spritesheetChildSize = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() * 4); auto spritesheetChildSize = ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeightWithSpacing() * 4);
ImGuiMultiSelectIO* io = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(),
anm2.content.spritesheets.size());
storage.ApplyRequests(io);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2()); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2());
storage.begin(anm2.content.spritesheets.size());
for (auto& [id, spritesheet] : anm2.content.spritesheets) for (auto& [id, spritesheet] : anm2.content.spritesheets)
{ {
ImGui::PushID(id); ImGui::PushID(id);
@@ -94,8 +90,8 @@ namespace anm2ed::spritesheets
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE); ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(spritesheet.path.c_str()); ImGui::TextUnformatted(spritesheet.path.c_str());
ImGui::PopFont(); ImGui::PopFont();
ImGui::TextUnformatted(std::format("ID: {}", id).c_str()); ImGui::Text("ID: %d", id);
ImGui::TextUnformatted(std::format("Size: {} x {}", texture.size.x, texture.size.y).c_str()); ImGui::Text("Size: %d x %d", texture.size.x, texture.size.y);
} }
ImGui::EndChild(); ImGui::EndChild();
@@ -120,7 +116,7 @@ namespace anm2ed::spritesheets
spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2)); spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE); if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
ImGui::TextUnformatted(std::format("#{} {}", id, spritesheet.path.string()).c_str()); ImGui::Text(SPRITESHEET_FORMAT, id, spritesheet.path.c_str());
if (isReferenced) ImGui::PopFont(); if (isReferenced) ImGui::PopFont();
} }
ImGui::EndChild(); ImGui::EndChild();
@@ -128,8 +124,7 @@ namespace anm2ed::spritesheets
ImGui::PopID(); ImGui::PopID();
} }
io = ImGui::EndMultiSelect(); storage.end();
storage.ApplyRequests(io);
ImGui::PopStyleVar(); ImGui::PopStyleVar();
} }
@@ -139,13 +134,16 @@ namespace anm2ed::spritesheets
auto rowOneWidgetSize = imgui::widget_size_with_row_get(4); auto rowOneWidgetSize = imgui::widget_size_with_row_get(4);
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", rowOneWidgetSize)) dialog.spritesheet_open(); if (ImGui::Button("Add", rowOneWidgetSize)) dialog.spritesheet_open();
imgui::set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd);
if (dialog.is_selected_file(dialog::SPRITESHEET_OPEN)) if (dialog.is_selected_file(dialog::SPRITESHEET_OPEN))
{ {
manager.spritesheet_add(dialog.path); int id{};
anm2.spritesheet_add(document.directory_get(), dialog.path, id);
selection = {id};
document.change(change::SPRITESHEETS);
dialog.reset(); dialog.reset();
} }
@@ -186,19 +184,19 @@ namespace anm2ed::spritesheets
ImGui::SameLine(); ImGui::SameLine();
auto unused = anm2.spritesheets_unused(); ImGui::BeginDisabled(unusedSpritesheetIDs.empty());
ImGui::BeginDisabled(unused.empty());
{ {
imgui::shortcut(settings.shortcutRemove, true); imgui::shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove Unused", rowOneWidgetSize)) if (ImGui::Button("Remove Unused", rowOneWidgetSize))
{ {
for (auto& id : unused) for (auto& id : unusedSpritesheetIDs)
{ {
Spritesheet& spritesheet = anm2.content.spritesheets[id]; Spritesheet& spritesheet = anm2.content.spritesheets[id];
toasts.add(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string())); toasts.add(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
anm2.spritesheet_remove(id); anm2.spritesheet_remove(id);
} }
unusedSpritesheetIDs.clear();
document.change(change::SPRITESHEETS);
} }
imgui::set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).", imgui::set_item_tooltip_shortcut("Remove all unused spritesheets (i.e., not used in any layer.).",
settings.shortcutRemove); settings.shortcutRemove);
@@ -207,7 +205,7 @@ namespace anm2ed::spritesheets
auto rowTwoWidgetSize = imgui::widget_size_with_row_get(3); auto rowTwoWidgetSize = imgui::widget_size_with_row_get(3);
imgui::shortcut(settings.shortcutSelectAll, true); imgui::shortcut(settings.shortcutSelectAll);
ImGui::BeginDisabled(selection.size() == anm2.content.spritesheets.size()); ImGui::BeginDisabled(selection.size() == anm2.content.spritesheets.size());
{ {
if (ImGui::Button("Select All", rowTwoWidgetSize)) if (ImGui::Button("Select All", rowTwoWidgetSize))
@@ -219,13 +217,11 @@ namespace anm2ed::spritesheets
ImGui::SameLine(); ImGui::SameLine();
imgui::shortcut(settings.shortcutSelectNone, true); imgui::shortcut(settings.shortcutSelectNone);
ImGui::BeginDisabled(selection.empty()); ImGui::BeginDisabled(selection.empty());
{ if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
}
ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone); imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
@@ -235,7 +231,8 @@ namespace anm2ed::spritesheets
{ {
for (auto& id : selection) for (auto& id : selection)
{ {
if (Spritesheet& spritesheet = anm2.content.spritesheets[id]; spritesheet.save(document.directory_get())) Spritesheet& spritesheet = anm2.content.spritesheets[id];
if (spritesheet.save(document.directory_get()))
toasts.add(std::format("Saved spritesheet #{}: {}", id, spritesheet.path.string())); toasts.add(std::format("Saved spritesheet #{}: {}", id, spritesheet.path.string()));
else else
toasts.add(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string())); toasts.add(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string()));

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include "dialog.h" #include "dialog.h"
#include "document_manager.h" #include "document.h"
#include "imgui.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
@@ -9,8 +10,11 @@ namespace anm2ed::spritesheets
{ {
class Spritesheets class Spritesheets
{ {
imgui::MultiSelectStorage storage{};
std::set<int> unusedSpritesheetIDs{};
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document::Document& document, settings::Settings& settings, resources::Resources& resources,
resources::Resources& resources, dialog::Dialog& dialog); dialog::Dialog& dialog);
}; };
} }

View File

@@ -60,10 +60,11 @@ namespace anm2ed::state
ImGui::NewFrame(); ImGui::NewFrame();
taskbar.update(settings, dialog, manager, isQuit); taskbar.update(settings, dialog, manager, isQuit);
documents.update(taskbar, manager, resources);
dockspace.update(taskbar, documents, manager, settings, resources, dialog, playback); dockspace.update(taskbar, documents, manager, settings, resources, dialog, playback);
toasts.update(); toasts.update();
documents.update(taskbar, manager, resources);
ImGui::GetStyle().FontScaleMain = settings.displayScale; ImGui::GetStyle().FontScaleMain = settings.displayScale;
SDL_GetWindowSize(window, &settings.windowSize.x, &settings.windowSize.y); SDL_GetWindowSize(window, &settings.windowSize.x, &settings.windowSize.y);
} }

View File

@@ -4,12 +4,10 @@
#include <ranges> #include <ranges>
#include "imgui.h" #include "imgui.h"
#include "types.h"
using namespace anm2ed::settings; using namespace anm2ed::settings;
using namespace anm2ed::dialog; using namespace anm2ed::dialog;
using namespace anm2ed::document_manager; using namespace anm2ed::document_manager;
using namespace anm2ed::imgui;
using namespace anm2ed::types; using namespace anm2ed::types;
namespace anm2ed::taskbar namespace anm2ed::taskbar
@@ -91,41 +89,25 @@ namespace anm2ed::taskbar
if (ImGui::BeginMenu("Settings")) if (ImGui::BeginMenu("Settings"))
{ {
if (ImGui::MenuItem("Configure")) isOpenConfigurePopup = true; if (ImGui::MenuItem("Configure")) configurePopup.open();
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("Help")) if (ImGui::BeginMenu("Help"))
{ {
if (ImGui::MenuItem("About")) isOpenAboutPopup = true; if (ImGui::MenuItem("About")) aboutPopup.open();
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
if (isOpenAboutPopup) configurePopup.trigger();
if (ImGui::BeginPopupModal(configurePopup.label, &configurePopup.isOpen, ImGuiWindowFlags_NoResize))
{ {
ImGui::OpenPopup("About"); auto childSize = imgui::size_without_footer_get(2);
isOpenAboutPopup = false;
}
if (isOpenConfigurePopup)
{
ImGui::OpenPopup("Configure");
editSettings = settings;
isOpenConfigurePopup = false;
}
auto viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * 0.5f));
if (ImGui::BeginPopupModal("Configure", nullptr, ImGuiWindowFlags_NoResize))
{
auto childSize = imgui::size_with_footer_get(2);
if (ImGui::BeginTabBar("##Configure Tabs")) if (ImGui::BeginTabBar("##Configure Tabs"))
{ {
@@ -230,7 +212,7 @@ namespace anm2ed::taskbar
if (ImGui::Button("Save", widgetSize)) if (ImGui::Button("Save", widgetSize))
{ {
settings = editSettings; settings = editSettings;
ImGui::CloseCurrentPopup(); configurePopup.close();
} }
ImGui::SetItemTooltip("Use the configured settings."); ImGui::SetItemTooltip("Use the configured settings.");
@@ -247,12 +229,11 @@ namespace anm2ed::taskbar
ImGui::EndPopup(); ImGui::EndPopup();
} }
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, ImVec2(0.5f, 0.5f)); aboutPopup.trigger();
ImGui::SetNextWindowSize(to_imvec2(to_vec2(viewport->Size) * 0.5f));
if (ImGui::BeginPopupModal("About", nullptr, ImGuiWindowFlags_NoResize)) if (ImGui::BeginPopupModal(aboutPopup.label, &aboutPopup.isOpen, ImGuiWindowFlags_NoResize))
{ {
if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); if (ImGui::Button("Close")) aboutPopup.close();
ImGui::EndPopup(); ImGui::EndPopup();
} }

View File

@@ -2,16 +2,17 @@
#include "dialog.h" #include "dialog.h"
#include "document_manager.h" #include "document_manager.h"
#include "imgui.h"
#include "settings.h" #include "settings.h"
namespace anm2ed::taskbar namespace anm2ed::taskbar
{ {
class Taskbar class Taskbar
{ {
bool isOpenConfigurePopup{}; imgui::PopupHelper configurePopup{imgui::PopupHelper("Configure")};
bool isOpenAboutPopup{}; imgui::PopupHelper aboutPopup{imgui::PopupHelper("About")};
int selectedShortcut{-1};
settings::Settings editSettings{}; settings::Settings editSettings{};
int selectedShortcut{};
public: public:
float height{}; float height{};

View File

@@ -68,7 +68,8 @@ namespace anm2ed::timeline
color = isActive ? ROOT_COLOR_ACTIVE : ROOT_COLOR; color = isActive ? ROOT_COLOR_ACTIVE : ROOT_COLOR;
break; break;
case anm2::LAYER: case anm2::LAYER:
label = std::format("#{} {}", id, anm2.content.layers[id].name); label = std::format("#{} {} (Spritesheet: #{})", id, anm2.content.layers.at(id).name,
anm2.content.layers[id].spritesheetID);
icon = icon::LAYER; icon = icon::LAYER;
color = isActive ? LAYER_COLOR_ACTIVE : LAYER_COLOR; color = isActive ? LAYER_COLOR_ACTIVE : LAYER_COLOR;
break; break;
@@ -165,10 +166,12 @@ namespace anm2ed::timeline
ImGui::SetCursorPos(cursorPos); ImGui::SetCursorPos(cursorPos);
ImGui::BeginDisabled();
ImGui::Text("(?)"); ImGui::Text("(?)");
ImGui::SetItemTooltip("%s", std::format(HELP_FORMAT, settings.shortcutNextFrame, settings.shortcutPreviousFrame, ImGui::SetItemTooltip("%s", std::format(HELP_FORMAT, settings.shortcutNextFrame, settings.shortcutPreviousFrame,
settings.shortcutShortenFrame, settings.shortcutExtendFrame) settings.shortcutShortenFrame, settings.shortcutExtendFrame)
.c_str()); .c_str());
ImGui::EndDisabled();
} }
} }
ImGui::EndChild(); ImGui::EndChild();
@@ -177,9 +180,11 @@ namespace anm2ed::timeline
index++; index++;
} }
void Timeline::items_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation, void Timeline::items_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources)
Settings& settings, Resources& resources)
{ {
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto itemsChildSize = ImVec2(ImGui::GetTextLineHeightWithSpacing() * 15, ImGui::GetContentRegionAvail().y); auto itemsChildSize = ImVec2(ImGui::GetTextLineHeightWithSpacing() * 15, ImGui::GetContentRegionAvail().y);
if (ImGui::BeginChild("##Items Child", itemsChildSize, ImGuiChildFlags_Borders)) if (ImGui::BeginChild("##Items Child", itemsChildSize, ImGuiChildFlags_Borders))
@@ -250,13 +255,24 @@ namespace anm2ed::timeline
auto widgetSize = imgui::widget_size_with_row_get(2); auto widgetSize = imgui::widget_size_with_row_get(2);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
ImGui::BeginDisabled(!animation); ImGui::BeginDisabled(!animation);
{ {
ImGui::Button("Add", widgetSize); if (ImGui::Button("Add", widgetSize)) propertiesPopup.open();
ImGui::SetItemTooltip("%s", "Add a new item to the animation.");
ImGui::SameLine(); ImGui::SameLine();
ImGui::Button("Remove", widgetSize);
ImGui::BeginDisabled(document.item_get());
{
ImGui::Button("Remove", widgetSize);
ImGui::SetItemTooltip("%s", "Remove the selected items from the animation.");
}
ImGui::EndDisabled();
} }
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::PopStyleVar();
} }
ImGui::EndChild(); ImGui::EndChild();
} }
@@ -317,10 +333,6 @@ namespace anm2ed::timeline
colorActiveHidden = to_imvec4(to_vec4(colorActive) * COLOR_HIDDEN_MULTIPLIER); colorActiveHidden = to_imvec4(to_vec4(colorActive) * COLOR_HIDDEN_MULTIPLIER);
colorHoveredHidden = to_imvec4(to_vec4(colorHovered) * COLOR_HIDDEN_MULTIPLIER); colorHoveredHidden = to_imvec4(to_vec4(colorHovered) * COLOR_HIDDEN_MULTIPLIER);
ImGui::PushStyleColor(ImGuiCol_Button, isVisible ? color : colorHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, isVisible ? colorActive : colorActiveHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, isVisible ? colorHovered : colorHoveredHidden);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders)) if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders))
@@ -420,11 +432,10 @@ namespace anm2ed::timeline
{ {
anm2::Reference frameReference = {reference.animationIndex, type, id, (int)i}; anm2::Reference frameReference = {reference.animationIndex, type, id, (int)i};
auto isSelected = baseReference == frameReference; auto isSelected = baseReference == frameReference;
auto isFrameVisible = isVisible && frame.isVisible;
frameTime += frame.delay; frameTime += frame.delay;
if (isSelected) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
ImGui::PushID(i); ImGui::PushID(i);
auto size = ImVec2(frameSize.x * frame.delay, frameSize.y); auto size = ImVec2(frameSize.x * frame.delay, frameSize.y);
@@ -435,12 +446,11 @@ namespace anm2ed::timeline
if (type == anm2::TRIGGER) if (type == anm2::TRIGGER)
ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * frame.atFrame, cursorPos.y)); ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * frame.atFrame, cursorPos.y));
if (!frame.isVisible) ImGui::PushStyleColor(ImGuiCol_Button, isFrameVisible ? color : colorHidden);
{ ImGui::PushStyleColor(ImGuiCol_ButtonActive, isFrameVisible ? colorActive : colorActiveHidden);
ImGui::PushStyleColor(ImGuiCol_Button, colorHidden); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, isFrameVisible ? colorHovered : colorHoveredHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, colorActiveHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, colorHoveredHidden); if (isSelected) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
}
if (ImGui::Button("##Frame Button", size)) if (ImGui::Button("##Frame Button", size))
{ {
@@ -451,7 +461,8 @@ namespace anm2ed::timeline
} }
if (type != anm2::TRIGGER) ImGui::SameLine(); if (type != anm2::TRIGGER) ImGui::SameLine();
if (!frame.isVisible) ImGui::PopStyleColor(3); ImGui::PopStyleColor(3);
if (isSelected) ImGui::PopStyleColor();
auto imageMin = ImVec2(ImGui::GetItemRectMin().x, auto imageMin = ImVec2(ImGui::GetItemRectMin().x,
ImGui::GetItemRectMax().y - (ImGui::GetItemRectSize().y / 2) - (imageSize.y / 2)); ImGui::GetItemRectMax().y - (ImGui::GetItemRectSize().y / 2) - (imageSize.y / 2));
@@ -459,15 +470,12 @@ namespace anm2ed::timeline
drawList->AddImage(resources.icons[icon].id, imageMin, imageMax); drawList->AddImage(resources.icons[icon].id, imageMin, imageMax);
if (isSelected) ImGui::PopStyleColor();
ImGui::PopID(); ImGui::PopID();
} }
} }
} }
ImGui::EndChild(); ImGui::EndChild();
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
index++; index++;
ImGui::PopID(); ImGui::PopID();
@@ -600,19 +608,19 @@ namespace anm2ed::timeline
auto label = playback.isPlaying ? "Pause" : "Play"; auto label = playback.isPlaying ? "Pause" : "Play";
auto tooltip = playback.isPlaying ? "Pause the animation." : "Play the animation."; auto tooltip = playback.isPlaying ? "Pause the animation." : "Play the animation.";
imgui::shortcut(settings.shortcutPlayPause, true); imgui::shortcut(settings.shortcutPlayPause);
if (ImGui::Button(label, widgetSize)) playback.toggle(); if (ImGui::Button(label, widgetSize)) playback.toggle();
imgui::set_item_tooltip_shortcut(tooltip, settings.shortcutPlayPause); imgui::set_item_tooltip_shortcut(tooltip, settings.shortcutPlayPause);
ImGui::SameLine(); ImGui::SameLine();
imgui::shortcut(settings.shortcutAdd, true); imgui::shortcut(settings.shortcutAdd);
ImGui::Button("Insert Frame", widgetSize); ImGui::Button("Insert Frame", widgetSize);
imgui::set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd); imgui::set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd);
ImGui::SameLine(); ImGui::SameLine();
imgui::shortcut(settings.shortcutRemove, true); imgui::shortcut(settings.shortcutRemove);
ImGui::Button("Delete Frame", widgetSize); ImGui::Button("Delete Frame", widgetSize);
imgui::set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove); imgui::set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
@@ -662,6 +670,187 @@ namespace anm2ed::timeline
ImGui::SetCursorPos(cursorPos); ImGui::SetCursorPos(cursorPos);
} }
void Timeline::popups(Document& document, anm2::Animation* animation, Settings& settings)
{
auto item_properties_reset = [&]()
{
addItemName.clear();
addItemSpritesheetID = {};
addItemID = -1;
isUnusedItemsSet = false;
};
auto& anm2 = document.anm2;
auto& reference = document.reference;
propertiesPopup.trigger();
if (ImGui::BeginPopupModal(propertiesPopup.label, &propertiesPopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto item_properties_close = [&]()
{
item_properties_reset();
propertiesPopup.close();
};
auto& type = settings.timelineAddItemType;
auto& locale = settings.timelineAddItemLocale;
auto& source = settings.timelineAddItemSource;
if (!isUnusedItemsSet)
{
unusedItems = type == anm2::LAYER ? anm2.layers_unused(reference)
: type == anm2::NULL_ ? anm2.nulls_unused(reference)
: std::set<int>{};
isUnusedItemsSet = true;
}
auto footerSize = imgui::footer_size_get();
auto optionsSize = imgui::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))
{
ImGui::SeparatorText("Type");
auto size = ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, ImGui::GetFrameHeightWithSpacing());
if (ImGui::BeginChild("Type Layer", size))
{
ImGui::RadioButton("Layer", &type, anm2::LAYER);
ImGui::SetItemTooltip("Layers are a basic visual element in an animation, used for displaying spritesheets.");
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Type Null", size))
{
ImGui::RadioButton("Null", &type, anm2::NULL_);
ImGui::SetItemTooltip(
"Nulls are invisible elements in an animation, used for interfacing with a game engine.");
}
ImGui::EndChild();
ImGui::SeparatorText("Source");
bool isNewOnly = unusedItems.empty();
if (isNewOnly) source = source::NEW;
if (ImGui::BeginChild("Source New", size))
{
ImGui::RadioButton("New", &source, source::NEW);
ImGui::SetItemTooltip("Create a new item to be used.");
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Source Existing", size))
{
ImGui::BeginDisabled(isNewOnly);
ImGui::RadioButton("Existing", &source, source::EXISTING);
ImGui::EndDisabled();
ImGui::SetItemTooltip("Use a pre-existing, presently unused item.");
}
ImGui::EndChild();
ImGui::SeparatorText("Locale");
if (ImGui::BeginChild("Locale Global", size))
{
ImGui::RadioButton("Global", &locale, locale::GLOBAL);
ImGui::SetItemTooltip("The item will be inserted into all animations, if not already present.");
}
ImGui::EndChild();
ImGui::SameLine();
if (ImGui::BeginChild("Locale Local", size))
{
ImGui::RadioButton("Local", &locale, locale::LOCAL);
ImGui::SetItemTooltip("The item will only be inserted into this animation.");
}
ImGui::EndChild();
ImGui::SeparatorText("Options");
ImGui::BeginDisabled(source == source::EXISTING);
{
imgui::input_text_string("Name", &addItemName);
ImGui::SetItemTooltip("Set the item's name.");
ImGui::BeginDisabled(type != anm2::LAYER);
{
auto spritesheets = anm2.spritesheet_names_get();
imgui::combo_strings("Spritesheet", &addItemSpritesheetID, spritesheets);
ImGui::SetItemTooltip("Set the layer item's spritesheet.");
}
ImGui::EndDisabled();
}
ImGui::EndDisabled();
}
ImGui::EndChild();
if (ImGui::BeginChild("Items", itemsSize, ImGuiChildFlags_Borders))
{
if (animation && source == source::EXISTING)
{
for (auto id : unusedItems)
{
auto isSelected = addItemID == id;
ImGui::PushID(id);
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;
}
else if (type == anm2::NULL_)
{
auto& null = anm2.content.nulls[id];
if (ImGui::Selectable(std::format("#{} {}", id, null.name).c_str(), isSelected)) addItemID = id;
}
ImGui::PopID();
}
}
}
ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2);
if (ImGui::Button("Add", widgetSize))
{
anm2::Reference addReference;
if (type == anm2::LAYER)
addReference = anm2.layer_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName,
addItemSpritesheetID, (locale::Type)locale);
else if (type == anm2::NULL_)
addReference =
anm2.null_add({reference.animationIndex, anm2::LAYER, addItemID}, addItemName, (locale::Type)locale);
reference = addReference;
item_properties_close();
}
ImGui::SetItemTooltip("Add the item, with the settings specified.");
ImGui::SameLine();
if (ImGui::Button("Cancel", widgetSize)) item_properties_close();
ImGui::SetItemTooltip("Cancel adding an item.");
ImGui::EndPopup();
}
}
void Timeline::update(DocumentManager& manager, Settings& settings, Resources& resources, Playback& playback) void Timeline::update(DocumentManager& manager, Settings& settings, Resources& resources, Playback& playback)
{ {
auto& document = *manager.get(); auto& document = *manager.get();
@@ -676,20 +865,28 @@ namespace anm2ed::timeline
{ {
isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
frames_child(document, animation, settings, resources, playback); frames_child(document, animation, settings, resources, playback);
items_child(anm2, reference, animation, settings, resources); items_child(document, animation, settings, resources);
} }
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::End(); ImGui::End();
if (imgui::shortcut(settings.shortcutPlayPause, false, true)) playback.toggle(); popups(document, animation, settings);
if (imgui::shortcut(settings.shortcutPlayPause, shortcut::GLOBAL)) playback.toggle();
if (animation) if (animation)
{ {
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutPreviousFrame))) if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutPreviousFrame)))
playback.decrement(animation->frameNum); {
playback.decrement(settings.playbackIsClampPlayhead ? animation->frameNum : anm2::FRAME_NUM_MAX);
reference.frameTime = playback.time;
}
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutNextFrame))) if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutNextFrame)))
playback.increment(animation->frameNum); {
playback.increment(settings.playbackIsClampPlayhead ? animation->frameNum : anm2::FRAME_NUM_MAX);
reference.frameTime = playback.time;
}
} }
if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutShortenFrame))) if (imgui::chord_repeating(imgui::string_to_chord(settings.shortcutShortenFrame)))

View File

@@ -3,6 +3,7 @@
#include "anm2.h" #include "anm2.h"
#include "document.h" #include "document.h"
#include "document_manager.h" #include "document_manager.h"
#include "imgui.h"
#include "playback.h" #include "playback.h"
#include "resources.h" #include "resources.h"
#include "settings.h" #include "settings.h"
@@ -14,20 +15,29 @@ namespace anm2ed::timeline
bool isDragging{}; bool isDragging{};
bool isWindowHovered{}; bool isWindowHovered{};
bool isHorizontalScroll{}; bool isHorizontalScroll{};
imgui::PopupHelper propertiesPopup{imgui::PopupHelper("Item Properties")};
std::string addItemName{};
int addItemSpritesheetID{};
bool addItemIsRect{};
int addItemID{-1};
bool isUnusedItemsSet{};
std::set<int> unusedItems{};
glm::vec2 scroll{}; glm::vec2 scroll{};
ImDrawList* pickerLineDrawList{}; ImDrawList* pickerLineDrawList{};
ImGuiStyle style{}; ImGuiStyle style{};
void item_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation, void item_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation,
settings::Settings& settings, resources::Resources& resources, anm2::Type type, int id, int& index); settings::Settings& settings, resources::Resources& resources, anm2::Type type, int id, int& index);
void items_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation, void items_child(Document& document, anm2::Animation* animation, settings::Settings& settings,
settings::Settings& settings, resources::Resources& resources); resources::Resources& resources);
void frame_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings, void frame_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings,
resources::Resources& resources, playback::Playback& playback, anm2::Type type, int id, int& index, resources::Resources& resources, playback::Playback& playback, anm2::Type type, int id, int& index,
float width); float width);
void frames_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings, void frames_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings,
resources::Resources& resources, playback::Playback& playback); resources::Resources& resources, playback::Playback& playback);
void popups(document::Document& document, anm2::Animation* animation, settings::Settings& settings);
public: public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings, void update(document_manager::DocumentManager& manager, settings::Settings& settings,
resources::Resources& resources, playback::Playback& playback); resources::Resources& resources, playback::Playback& playback);

View File

@@ -55,7 +55,7 @@ namespace anm2ed::tools
auto member = SHORTCUT_MEMBERS[info.shortcut]; auto member = SHORTCUT_MEMBERS[info.shortcut];
if (imgui::shortcut(settings.*member, false, true, i == tool::COLOR ? false : true)) tool_switch((tool::Type)i); if (imgui::shortcut(settings.*member, shortcut::GLOBAL_SET)) tool_switch((tool::Type)i);
if (i == tool::COLOR) if (i == tool::COLOR)
{ {

View File

@@ -4,6 +4,50 @@
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <imgui/imgui.h> #include <imgui/imgui.h>
namespace anm2ed::types::change
{
enum Type
{
LAYERS,
NULLS,
SPRITESHEETS,
EVENTS,
ANIMATIONS,
ITEMS,
FRAMES,
COUNT
};
}
namespace anm2ed::types::shortcut
{
enum Type
{
FOCUSED,
GLOBAL,
FOCUSED_SET,
GLOBAL_SET
};
}
namespace anm2ed::types::locale
{
enum Type
{
LOCAL,
GLOBAL
};
}
namespace anm2ed::types::source
{
enum Type
{
NEW,
EXISTING
};
}
namespace anm2ed::types::merge namespace anm2ed::types::merge
{ {
enum Type enum Type