Refactoring; structs for popups/multi-selects, additional popups in places
This commit is contained in:
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"C_Cpp.formatting": "clangFormat",
|
||||
"editor.formatOnSave": true,
|
||||
"clang-format.style": "file",
|
||||
"clangd.arguments": [
|
||||
"--compile-commands-dir=build"
|
||||
]
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
139
src/anm2.cpp
139
src/anm2.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
20
src/anm2.h
20
src/anm2.h
@@ -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();
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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.)",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
46
src/imgui.h
46
src/imgui.h
@@ -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();
|
||||
};
|
||||
}
|
||||
127
src/layers.cpp
127
src/layers.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
13
src/layers.h
13
src/layers.h
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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.)",
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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{};
|
||||
|
||||
253
src/timeline.cpp
253
src/timeline.cpp
@@ -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)))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
44
src/types.h
44
src/types.h
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user