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

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

View File

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

View File

@@ -16,6 +16,7 @@ using namespace anm2ed::util;
namespace anm2ed::anm2
{
void Reference::previous_frame(int 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> used;
@@ -778,7 +765,16 @@ namespace anm2ed::anm2
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)
@@ -786,24 +782,60 @@ namespace anm2ed::anm2
return map::find(content.spritesheets, id);
}
void Anm2::spritesheet_remove(int id)
{
content.spritesheet_remove(id);
}
std::set<int> Anm2::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)
@@ -811,45 +843,68 @@ namespace anm2ed::anm2
content.event_add(id);
}
std::set<int> Anm2::events_unused()
std::set<int> Anm2::events_unused(Reference reference)
{
std::set<int> used;
for (auto& animation : animations.items)
for (auto& frame : animation.triggers.frames)
used.insert(frame.eventID);
std::set<int> used{};
std::set<int> unused{};
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)
if (!used.contains(id)) unused.insert(id);
return unused;
}
std::set<int> Anm2::layers_unused()
std::set<int> Anm2::layers_unused(Reference reference)
{
std::set<int> used;
for (auto& animation : animations.items)
for (auto& id : animation.layerAnimations | std::views::keys)
used.insert(id);
std::set<int> used{};
std::set<int> unused{};
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)
if (!used.contains(id)) unused.insert(id);
return unused;
}
std::set<int> Anm2::nulls_unused()
std::set<int> Anm2::nulls_unused(Reference reference)
{
std::set<int> used;
for (auto& animation : animations.items)
for (auto& id : animation.nullAnimations | std::views::keys)
used.insert(id);
std::set<int> used{};
std::set<int> unused{};
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)
if (!used.contains(id)) unused.insert(id);
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 LAYER_FORMAT = "#{} {} (Spritesheet: #{})";
constexpr auto NULL_FORMAT = "#{} {}";
constexpr auto SPRITESHEET_FORMAT = "#%d %s";
enum Type
{
NONE,
@@ -44,6 +48,8 @@ namespace anm2ed::anm2
auto operator<=>(const Reference&) const = default;
};
constexpr anm2::Reference REFERENCE_DEFAULT = {-1, anm2::NONE, -1, -1, -1};
class Info
{
public:
@@ -228,11 +234,15 @@ namespace anm2ed::anm2
Spritesheet* spritesheet_get(int id);
void spritesheet_remove(int id);
std::set<int> spritesheets_unused();
void layer_add(int& id);
void null_add(int& id);
int layer_add();
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);
std::set<int> events_unused();
std::set<int> layers_unused();
std::set<int> nulls_unused();
std::set<int> events_unused(Reference reference = REFERENCE_DEFAULT);
std::set<int> layers_unused(Reference reference = REFERENCE_DEFAULT);
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 (manager.get())
if (auto document = manager.get(); document)
{
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.windowIsFrameProperties) frameProperties.update(manager, settings);
if (settings.windowIsLayers) layers.update(manager, settings, resources);
if (settings.windowIsNulls) nulls.update(manager, settings, resources);
if (settings.windowIsLayers) layers.update(*document, settings, resources);
if (settings.windowIsNulls) nulls.update(*document, manager.selected, settings, resources);
if (settings.windowIsOnionskin) onionskin.update(settings);
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.windowIsTools) tools.update(settings, resources);
}

View File

@@ -5,13 +5,21 @@
using namespace anm2ed::anm2;
using namespace anm2ed::filesystem;
using namespace anm2ed::types;
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)
{
for (auto& value : isJustChanged)
value = true;
if (!path_is_exist(path)) return;
if (isNew)
@@ -23,7 +31,8 @@ namespace anm2ed::document
}
this->path = path;
hash_set();
on_change();
clean();
}
bool Document::save(const std::string& path, std::string* errorString)
@@ -32,7 +41,7 @@ namespace anm2ed::document
if (anm2.serialize(this->path, errorString))
{
hash_set();
clean();
return true;
}
@@ -40,18 +49,20 @@ namespace anm2ed::document
}
void Document::hash_set()
{
hash = anm2.hash();
}
void Document::clean()
{
saveHash = anm2.hash();
hash = saveHash;
}
void Document::hash_time(double time, double interval)
void Document::change(change::Type type)
{
if (time - lastHashTime > interval)
{
hash = anm2.hash();
lastHashTime = time;
}
hash_set();
isJustChanged[type] = true;
}
bool Document::is_dirty()
@@ -59,6 +70,11 @@ namespace anm2ed::document
return hash != saveHash;
}
bool Document::is_just_changed(types::change::Type type)
{
return isJustChanged[type];
}
std::string Document::directory_get()
{
return path.parent_path();
@@ -79,6 +95,11 @@ namespace anm2ed::document
return anm2.frame_get(reference);
}
anm2::Item* Document::item_get()
{
return anm2.item_get(reference);
}
anm2::Spritesheet* Document::spritesheet_get()
{
return anm2.spritesheet_get(referenceSpritesheet);
@@ -88,4 +109,23 @@ namespace anm2ed::document
{
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
#include "anm2.h"
#include <filesystem>
#include <set>
#include "anm2.h"
#include "types.h"
#include <glm/glm.hpp>
namespace anm2ed::document
@@ -22,6 +24,7 @@ namespace anm2ed::document
int overlayIndex{};
int referenceSpritesheet{-1};
int referenceLayer{-1};
std::set<int> selectedEvents{};
std::set<int> selectedLayers{};
@@ -29,9 +32,12 @@ namespace anm2ed::document
std::set<int> selectedAnimations{};
std::set<int> selectedSpritesheets{};
std::vector<std::string> spritesheetNames{};
std::vector<const char*> spritesheetNamesCstr{};
uint64_t hash{};
uint64_t saveHash{};
double lastHashTime{};
bool isJustChanged[types::change::COUNT]{};
bool isOpen{true};
Document();
@@ -39,12 +45,17 @@ namespace anm2ed::document
Document(const std::string& path, bool isNew = false, std::string* errorString = nullptr);
bool save(const std::string& path = {}, std::string* errorString = nullptr);
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();
void update();
std::string directory_get();
std::string filename_get();
anm2::Animation* animation_get();
anm2::Frame* frame_get();
anm2::Item* item_get();
anm2::Spritesheet* spritesheet_get();
bool is_valid();
};

View File

@@ -1,9 +1,7 @@
#include "document_manager.h"
#include "toast.h"
#include "util.h"
using namespace anm2ed::toast;
using namespace anm2ed::util;
namespace anm2ed::document_manager
@@ -27,10 +25,8 @@ namespace anm2ed::document_manager
documents.emplace_back(std::move(document));
selected = documents.size() - 1;
pendingSelected = selected;
toasts.add(std::format("Opened document: {}", path));
return true;
}
toasts.add_error(std::format("Failed to open document: {} ({})", path, errorString));
return false;
}
@@ -47,10 +43,7 @@ namespace anm2ed::document_manager
document->path = !path.empty() ? path : document->path.string();
if (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));
document->save(document->path, &errorString);
}
void DocumentManager::save(const std::string& path)
@@ -62,15 +55,4 @@ namespace anm2ed::document_manager
{
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))
{
auto isDirty = document.is_dirty();
auto isRequested = i == manager.pendingSelected;
auto isSelected = i == manager.selected;
if (isSelected) document.hash_time(ImGui::GetTime());
auto isRequested = i == manager.pendingSelected;
auto font = isDirty ? font::ITALICS : font::REGULAR;
@@ -67,23 +65,18 @@ namespace anm2ed::documents
else
manager.close(i);
}
if (isSelected) document.update();
}
ImGui::EndTabBar();
}
if (isOpenCloseDocumentPopup)
{
ImGui::OpenPopup("Close Document");
isOpenCloseDocumentPopup = false;
}
closePopup.trigger();
if (isCloseDocument)
{
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_None, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowSize(ImVec2(), ImGuiCond_None);
if (ImGui::BeginPopupModal("Close Document", nullptr, ImGuiWindowFlags_NoResize))
if (ImGui::BeginPopupModal(closePopup.label, &closePopup.isOpen, ImGuiWindowFlags_NoResize))
{
auto closeDocument = manager.get(closeDocumentIndex);
@@ -96,8 +89,7 @@ namespace anm2ed::documents
auto close = [&]()
{
closeDocumentIndex = 0;
isCloseDocument = false;
ImGui::CloseCurrentPopup();
closePopup.close();
};
if (ImGui::Button("Yes", widgetSize))

View File

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

View File

@@ -2,8 +2,6 @@
#include <ranges>
#include "imgui.h"
using namespace anm2ed::document_manager;
using namespace anm2ed::settings;
using namespace anm2ed::resources;
@@ -13,22 +11,22 @@ namespace anm2ed::events
{
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))
{
auto document = manager.get();
anm2::Anm2& anm2 = document->anm2;
auto& selection = document->selectedEvents;
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
auto childSize = imgui::size_with_footer_get();
auto childSize = imgui::size_without_footer_get();
bool isRenamed{};
if (ImGui::BeginChild("##Events Child", childSize, true))
{
ImGuiMultiSelectIO* io =
ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), anm2.content.events.size());
storage.ApplyRequests(io);
storage.begin(anm2.content.events.size());
for (auto& [id, event] : anm2.content.events)
{
@@ -36,8 +34,9 @@ namespace anm2ed::events
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
event.name, isSelected);
if (imgui::selectable_input_text(event.name, std::format("###Document #{} Event #{}", manager.selected, id),
event.name, isSelected, 0, &isRenamed))
if (isRenamed) document.change(change::EVENTS);
if (ImGui::BeginItemTooltip())
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
@@ -48,31 +47,33 @@ namespace anm2ed::events
ImGui::PopID();
}
io = ImGui::EndMultiSelect();
storage.ApplyRequests(io);
storage.end();
}
ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutAdd, true);
imgui::shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize))
{
int id{};
anm2.event_add(id);
selection = {id};
document.change(change::EVENTS);
}
imgui::set_item_tooltip_shortcut("Add an event.", settings.shortcutAdd);
ImGui::SameLine();
std::set<int> unusedEventIDs = anm2.events_unused();
imgui::shortcut(settings.shortcutRemove, true);
imgui::shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unusedEventIDs.empty());
{
if (ImGui::Button("Remove Unused", widgetSize))
{
for (auto& id : unusedEventIDs)
anm2.content.layers.erase(id);
anm2.content.events.erase(id);
document.change(change::EVENTS);
unusedEventIDs.clear();
}
}
ImGui::EndDisabled();
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
#include "document_manager.h"
#include "imgui.h"
#include "resources.h"
#include "settings.h"
@@ -8,7 +9,8 @@ namespace anm2ed::events
{
class Events
{
ImGuiSelectionExternalStorage storage{};
imgui::MultiSelectStorage storage{};
std::set<int> unusedEventIDs{};
public:
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.");
ImGui::SameLine();
ImGui::EndDisabled();
ImGui::Checkbox("Round", &settings.propertiesIsRound);
ImGui::BeginDisabled(!frame);
ImGui::SetItemTooltip(
"%s", "When toggled, decimal values will be snapped to their nearest whole value when changed.");

View File

@@ -6,8 +6,12 @@
#include <sstream>
#include <unordered_map>
using namespace anm2ed::types;
using namespace glm;
namespace anm2ed::imgui
{
std::string chord_to_string(ImGuiKeyChord chord)
{
std::string result;
@@ -71,14 +75,19 @@ namespace anm2ed::imgui
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));
}
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));
}
@@ -107,8 +116,13 @@ namespace anm2ed::imgui
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,
ImGuiSelectableFlags flags)
ImGuiSelectableFlags flags, bool* isRenamed)
{
static std::string editID{};
static bool isJustEdit{};
@@ -128,6 +142,7 @@ namespace anm2ed::imgui
{
editID.clear();
isActivated = true;
if (isRenamed) *isRenamed = true;
}
if (ImGui::IsItemDeactivatedAfterEdit() || ImGui::IsKeyPressed(ImGuiKey_Escape)) editID.clear();
}
@@ -146,10 +161,9 @@ namespace anm2ed::imgui
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", text.c_str());
ImGui::SetItemTooltip("%s\n(Shortcut: %s)", tooltip, shortcut.c_str());
}
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected)
@@ -219,17 +233,71 @@ namespace anm2ed::imgui
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;
auto flags = isGlobal ? ImGuiInputFlags_RouteGlobal : ImGuiInputFlags_RouteFocused;
if (isSet)
if (ImGui::GetTopMostPopupModal() != nullptr) return false;
auto flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
: ImGuiInputFlags_RouteFocused;
if (type == shortcut::GLOBAL_SET || type == shortcut::FOCUSED_SET)
{
ImGui::SetNextItemShortcut(string_to_chord(string), flags);
return true;
return false;
}
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
#include <imgui/imgui.h>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
#include "types.h"
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},
{"B", ImGuiKey_B},
{"C", ImGuiKey_C},
@@ -122,17 +129,46 @@ namespace anm2ed::imgui
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);
float footer_height_get(int itemCount = 1);
ImVec2 size_with_footer_get(int rowCount = 1);
ImVec2 child_size_get(int rowCount = 1, bool isContentRegionAvail = false);
ImVec2 footer_size_get(int itemCount = 1);
ImVec2 size_without_footer_get(int rowCount = 1);
ImVec2 child_size_get(int rowCount = 1);
int input_text_callback(ImGuiInputTextCallbackData* data);
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<const char*>& strings);
bool selectable_input_text(const std::string& label, const std::string& id, std::string& text,
bool isSelected = false, ImGuiSelectableFlags flags = 0);
void set_item_tooltip_shortcut(const std::string& tooltip, const std::string& shortcut = {});
bool isSelected = false, ImGuiSelectableFlags flags = 0, bool* isRenamed = nullptr);
void set_item_tooltip_shortcut(const char* tooltip, const std::string& shortcut = {});
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected);
ImVec2 icon_size_get();
bool chord_held(ImGuiKeyChord chord);
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 "imgui.h"
#include "util.h"
using namespace anm2ed::document_manager;
using namespace anm2ed::document;
using namespace anm2ed::settings;
using namespace anm2ed::resources;
using namespace anm2ed::types;
using namespace anm2ed::util;
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))
{
auto document = manager.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();
auto childSize = imgui::size_without_footer_get();
if (ImGui::BeginChild("##Layers Child", childSize, true))
{
ImGuiMultiSelectIO* io =
ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape, selection.size(), anm2.content.layers.size());
storage.ApplyRequests(io);
storage.begin(anm2.content.layers.size());
for (auto& [id, layer] : anm2.content.layers)
{
auto isSelected = selection.contains(id);
auto isReferenced = referenceLayer == id;
ImGui::PushID(id);
ImGui::SetNextItemSelectionUserData(id);
imgui::selectable_input_text(std::format("#{} {}", id, layer.name),
std::format("###Document #{} Layer #{}", manager.selected, id), layer.name,
isSelected);
if (isReferenced) ImGui::PushFont(resources.fonts[font::ITALICS].get(), font::SIZE);
ImGui::Selectable(std::format("#{} {} (Spritesheet: #{})", id, layer.name, layer.spritesheetID).c_str(),
isSelected);
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
referenceLayer = id;
properties_popup_open(id);
}
if (isReferenced) ImGui::PopFont();
if (ImGui::BeginItemTooltip())
{
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
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::Text("ID: %d", id);
ImGui::Text("Spritesheet ID: %d", layer.spritesheetID);
ImGui::EndTooltip();
}
ImGui::PopID();
}
io = ImGui::EndMultiSelect();
storage.ApplyRequests(io);
storage.end();
}
ImGui::EndChild();
auto widgetSize = imgui::widget_size_with_row_get(2);
imgui::shortcut(settings.shortcutAdd, true);
ImGui::Button("Add", widgetSize);
imgui::shortcut(settings.shortcutAdd);
if (ImGui::Button("Add", widgetSize)) properties_popup_open();
imgui::set_item_tooltip_shortcut("Add a layer.", settings.shortcutAdd);
ImGui::SameLine();
std::set<int> unusedLayersIDs = anm2.layers_unused();
imgui::shortcut(settings.shortcutRemove, true);
ImGui::BeginDisabled(unusedLayersIDs.empty());
imgui::shortcut(settings.shortcutRemove);
ImGui::BeginDisabled(unusedLayerIDs.empty());
{
if (ImGui::Button("Remove Unused", widgetSize))
for (auto& id : unusedLayersIDs)
{
for (auto& id : unusedLayerIDs)
anm2.content.layers.erase(id);
document.change(change::LAYERS);
unusedLayerIDs.clear();
}
}
ImGui::EndDisabled();
imgui::set_item_tooltip_shortcut("Remove unused layers (i.e., ones not used in any animation.)",
settings.shortcutRemove);
}
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
#include "document_manager.h"
#include "document.h"
#include "resources.h"
#include "settings.h"
#include "imgui.h"
namespace anm2ed::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:
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
resources::Resources& resources);
void update(document::Document& document, settings::Settings& settings, resources::Resources& resources);
};
}

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
#include "imgui.h"
using namespace anm2ed::settings;
using namespace anm2ed::types;
using namespace glm;
namespace anm2ed::onionskin
@@ -23,7 +24,7 @@ namespace anm2ed::onionskin
ImGui::PopID();
};
imgui::shortcut(settings.shortcutOnionskin, true, true);
imgui::shortcut(settings.shortcutOnionskin);
ImGui::Checkbox("Enabled", &settings.onionskinIsEnabled);
order_configure("Before", settings.onionskinBeforeCount, settings.onionskinBeforeColor);
@@ -36,7 +37,8 @@ namespace anm2ed::onionskin
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();
}

View File

@@ -120,6 +120,8 @@ namespace anm2ed::settings
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_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(ONIONSKIN_IS_ENABLED, onionskinIsEnabled, "Enabled", BOOL, false) \

View File

@@ -1,35 +1,33 @@
#include "spritesheets.h"
#include <ranges>
#include "imgui.h"
#include "toast.h"
#include "types.h"
#include <ranges>
using namespace anm2ed::anm2;
using namespace anm2ed::settings;
using namespace anm2ed::resources;
using namespace anm2ed::dialog;
using namespace anm2ed::document_manager;
using namespace anm2ed::document;
using namespace anm2ed::types;
using namespace anm2ed::toast;
using namespace glm;
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))
{
auto& document = *manager.get();
auto& anm2 = document.anm2;
auto& selection = document.selectedSpritesheets;
auto style = ImGui::GetStyle();
static ImGuiSelectionExternalStorage storage{};
storage.UserData = &selection;
storage.AdapterSetItemSelected = imgui::external_storage_set;
storage.user_data_set(&selection);
auto childSize = imgui::size_with_footer_get(2);
auto childSize = imgui::size_without_footer_get(2);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
@@ -37,12 +35,10 @@ namespace anm2ed::spritesheets
{
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());
storage.begin(anm2.content.spritesheets.size());
for (auto& [id, spritesheet] : anm2.content.spritesheets)
{
ImGui::PushID(id);
@@ -94,8 +90,8 @@ namespace anm2ed::spritesheets
ImGui::PushFont(resources.fonts[font::BOLD].get(), font::SIZE);
ImGui::TextUnformatted(spritesheet.path.c_str());
ImGui::PopFont();
ImGui::TextUnformatted(std::format("ID: {}", id).c_str());
ImGui::TextUnformatted(std::format("Size: {} x {}", texture.size.x, texture.size.y).c_str());
ImGui::Text("ID: %d", id);
ImGui::Text("Size: %d x %d", texture.size.x, texture.size.y);
}
ImGui::EndChild();
@@ -120,7 +116,7 @@ namespace anm2ed::spritesheets
spritesheetChildSize.y - spritesheetChildSize.y / 2 - ImGui::GetTextLineHeight() / 2));
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();
}
ImGui::EndChild();
@@ -128,8 +124,7 @@ namespace anm2ed::spritesheets
ImGui::PopID();
}
io = ImGui::EndMultiSelect();
storage.ApplyRequests(io);
storage.end();
ImGui::PopStyleVar();
}
@@ -139,13 +134,16 @@ namespace anm2ed::spritesheets
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();
imgui::set_item_tooltip_shortcut("Add a new spritesheet.", settings.shortcutAdd);
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();
}
@@ -186,19 +184,19 @@ namespace anm2ed::spritesheets
ImGui::SameLine();
auto unused = anm2.spritesheets_unused();
ImGui::BeginDisabled(unused.empty());
ImGui::BeginDisabled(unusedSpritesheetIDs.empty());
{
imgui::shortcut(settings.shortcutRemove, true);
imgui::shortcut(settings.shortcutRemove);
if (ImGui::Button("Remove Unused", rowOneWidgetSize))
{
for (auto& id : unused)
for (auto& id : unusedSpritesheetIDs)
{
Spritesheet& spritesheet = anm2.content.spritesheets[id];
toasts.add(std::format("Removed spritesheet #{}: {}", id, spritesheet.path.string()));
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.).",
settings.shortcutRemove);
@@ -207,7 +205,7 @@ namespace anm2ed::spritesheets
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());
{
if (ImGui::Button("Select All", rowTwoWidgetSize))
@@ -219,13 +217,11 @@ namespace anm2ed::spritesheets
ImGui::SameLine();
imgui::shortcut(settings.shortcutSelectNone, true);
imgui::shortcut(settings.shortcutSelectNone);
ImGui::BeginDisabled(selection.empty());
{
if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
}
ImGui::EndDisabled();
if (ImGui::Button("Select None", rowTwoWidgetSize)) selection.clear();
imgui::set_item_tooltip_shortcut("Unselect all spritesheets.", settings.shortcutSelectNone);
ImGui::EndDisabled();
ImGui::SameLine();
@@ -235,7 +231,8 @@ namespace anm2ed::spritesheets
{
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()));
else
toasts.add(std::format("Unable to save spritesheet #{}: {}", id, spritesheet.path.string()));

View File

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

View File

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

View File

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

View File

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

View File

@@ -68,7 +68,8 @@ namespace anm2ed::timeline
color = isActive ? ROOT_COLOR_ACTIVE : ROOT_COLOR;
break;
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;
color = isActive ? LAYER_COLOR_ACTIVE : LAYER_COLOR;
break;
@@ -165,10 +166,12 @@ namespace anm2ed::timeline
ImGui::SetCursorPos(cursorPos);
ImGui::BeginDisabled();
ImGui::Text("(?)");
ImGui::SetItemTooltip("%s", std::format(HELP_FORMAT, settings.shortcutNextFrame, settings.shortcutPreviousFrame,
settings.shortcutShortenFrame, settings.shortcutExtendFrame)
.c_str());
ImGui::EndDisabled();
}
}
ImGui::EndChild();
@@ -177,9 +180,11 @@ namespace anm2ed::timeline
index++;
}
void Timeline::items_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation,
Settings& settings, Resources& resources)
void Timeline::items_child(Document& document, anm2::Animation* animation, Settings& settings, Resources& resources)
{
auto& anm2 = document.anm2;
auto& reference = document.reference;
auto itemsChildSize = ImVec2(ImGui::GetTextLineHeightWithSpacing() * 15, ImGui::GetContentRegionAvail().y);
if (ImGui::BeginChild("##Items Child", itemsChildSize, ImGuiChildFlags_Borders))
@@ -250,13 +255,24 @@ namespace anm2ed::timeline
auto widgetSize = imgui::widget_size_with_row_get(2);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.WindowPadding);
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::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::PopStyleVar();
}
ImGui::EndChild();
}
@@ -317,10 +333,6 @@ namespace anm2ed::timeline
colorActiveHidden = to_imvec4(to_vec4(colorActive) * 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());
if (ImGui::BeginChild("##Frames Child", childSize, ImGuiChildFlags_Borders))
@@ -420,11 +432,10 @@ namespace anm2ed::timeline
{
anm2::Reference frameReference = {reference.animationIndex, type, id, (int)i};
auto isSelected = baseReference == frameReference;
auto isFrameVisible = isVisible && frame.isVisible;
frameTime += frame.delay;
if (isSelected) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
ImGui::PushID(i);
auto size = ImVec2(frameSize.x * frame.delay, frameSize.y);
@@ -435,12 +446,11 @@ namespace anm2ed::timeline
if (type == anm2::TRIGGER)
ImGui::SetCursorPos(ImVec2(cursorPos.x + frameSize.x * frame.atFrame, cursorPos.y));
if (!frame.isVisible)
{
ImGui::PushStyleColor(ImGuiCol_Button, colorHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, colorActiveHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, colorHoveredHidden);
}
ImGui::PushStyleColor(ImGuiCol_Button, isFrameVisible ? color : colorHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, isFrameVisible ? colorActive : colorActiveHidden);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, isFrameVisible ? colorHovered : colorHoveredHidden);
if (isSelected) ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
if (ImGui::Button("##Frame Button", size))
{
@@ -451,7 +461,8 @@ namespace anm2ed::timeline
}
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,
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);
if (isSelected) ImGui::PopStyleColor();
ImGui::PopID();
}
}
}
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);
index++;
ImGui::PopID();
@@ -600,19 +608,19 @@ namespace anm2ed::timeline
auto label = playback.isPlaying ? "Pause" : "Play";
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();
imgui::set_item_tooltip_shortcut(tooltip, settings.shortcutPlayPause);
ImGui::SameLine();
imgui::shortcut(settings.shortcutAdd, true);
imgui::shortcut(settings.shortcutAdd);
ImGui::Button("Insert Frame", widgetSize);
imgui::set_item_tooltip_shortcut("Insert a frame, based on the current selection.", settings.shortcutAdd);
ImGui::SameLine();
imgui::shortcut(settings.shortcutRemove, true);
imgui::shortcut(settings.shortcutRemove);
ImGui::Button("Delete Frame", widgetSize);
imgui::set_item_tooltip_shortcut("Delete the selected frames.", settings.shortcutRemove);
@@ -662,6 +670,187 @@ namespace anm2ed::timeline
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)
{
auto& document = *manager.get();
@@ -676,20 +865,28 @@ namespace anm2ed::timeline
{
isWindowHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
frames_child(document, animation, settings, resources, playback);
items_child(anm2, reference, animation, settings, resources);
items_child(document, animation, settings, resources);
}
ImGui::PopStyleVar();
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 (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)))
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)))

View File

@@ -3,6 +3,7 @@
#include "anm2.h"
#include "document.h"
#include "document_manager.h"
#include "imgui.h"
#include "playback.h"
#include "resources.h"
#include "settings.h"
@@ -14,20 +15,29 @@ namespace anm2ed::timeline
bool isDragging{};
bool isWindowHovered{};
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{};
ImDrawList* pickerLineDrawList{};
ImGuiStyle style{};
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);
void items_child(anm2::Anm2& anm2, anm2::Reference& reference, anm2::Animation* animation,
settings::Settings& settings, resources::Resources& resources);
void items_child(Document& document, anm2::Animation* animation, settings::Settings& settings,
resources::Resources& resources);
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,
float width);
void frames_child(document::Document& document, anm2::Animation* animation, settings::Settings& settings,
resources::Resources& resources, playback::Playback& playback);
void popups(document::Document& document, anm2::Animation* animation, settings::Settings& settings);
public:
void update(document_manager::DocumentManager& manager, settings::Settings& settings,
resources::Resources& resources, playback::Playback& playback);

View File

@@ -55,7 +55,7 @@ namespace anm2ed::tools
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)
{

View File

@@ -4,6 +4,50 @@
#include <glm/gtc/type_ptr.hpp>
#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
{
enum Type