added item selection
This commit is contained in:
@@ -44,6 +44,7 @@ namespace anm2ed
|
|||||||
Storage& null = current.null;
|
Storage& null = current.null;
|
||||||
Storage& sound = current.sound;
|
Storage& sound = current.sound;
|
||||||
Storage& spritesheet = current.spritesheet;
|
Storage& spritesheet = current.spritesheet;
|
||||||
|
Storage& items = current.items;
|
||||||
Storage& frames = current.frames;
|
Storage& frames = current.frames;
|
||||||
std::string& message = current.message;
|
std::string& message = current.message;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace anm2ed::imgui
|
namespace anm2ed::imgui
|
||||||
{
|
{
|
||||||
|
static auto isRenaming = false;
|
||||||
|
|
||||||
constexpr ImVec4 COLOR_LIGHT_BUTTON{0.98f, 0.98f, 0.98f, 1.0f};
|
constexpr ImVec4 COLOR_LIGHT_BUTTON{0.98f, 0.98f, 0.98f, 1.0f};
|
||||||
constexpr ImVec4 COLOR_LIGHT_TITLE_BG{0.78f, 0.78f, 0.78f, 1.0f};
|
constexpr ImVec4 COLOR_LIGHT_TITLE_BG{0.78f, 0.78f, 0.78f, 1.0f};
|
||||||
constexpr ImVec4 COLOR_LIGHT_TITLE_BG_ACTIVE{0.64f, 0.64f, 0.64f, 1.0f};
|
constexpr ImVec4 COLOR_LIGHT_TITLE_BG_ACTIVE{0.64f, 0.64f, 0.64f, 1.0f};
|
||||||
@@ -127,6 +129,7 @@ namespace anm2ed::imgui
|
|||||||
editID.clear();
|
editID.clear();
|
||||||
isActivated = true;
|
isActivated = true;
|
||||||
state = RENAME_FINISHED;
|
state = RENAME_FINISHED;
|
||||||
|
isRenaming = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state == RENAME_BEGIN)
|
if (state == RENAME_BEGIN)
|
||||||
@@ -150,6 +153,7 @@ namespace anm2ed::imgui
|
|||||||
state = RENAME_BEGIN;
|
state = RENAME_BEGIN;
|
||||||
editID = id;
|
editID = id;
|
||||||
isActivated = true;
|
isActivated = true;
|
||||||
|
isRenaming = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,11 +211,12 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected)
|
void external_storage_set(ImGuiSelectionExternalStorage* self, int id, bool isSelected)
|
||||||
{
|
{
|
||||||
auto* set = (std::set<int>*)self->UserData;
|
auto* storage = static_cast<MultiSelectStorage*>(self->UserData);
|
||||||
|
auto value = storage ? storage->resolve_index(id) : id;
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
set->insert(id);
|
storage->insert(value);
|
||||||
else
|
else
|
||||||
set->erase(id);
|
storage->erase(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string chord_to_string(ImGuiKeyChord chord)
|
std::string chord_to_string(ImGuiKeyChord chord)
|
||||||
@@ -351,8 +356,7 @@ namespace anm2ed::imgui
|
|||||||
bool shortcut(ImGuiKeyChord chord, shortcut::Type type, bool isRepeat)
|
bool shortcut(ImGuiKeyChord chord, shortcut::Type type, bool isRepeat)
|
||||||
{
|
{
|
||||||
if (ImGui::GetTopMostPopupModal() != nullptr) return false;
|
if (ImGui::GetTopMostPopupModal() != nullptr) return false;
|
||||||
|
if (isRepeat && !isRenaming) return chord_repeating(chord);
|
||||||
if (isRepeat) return chord_repeating(chord);
|
|
||||||
|
|
||||||
int flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
|
int flags = type == shortcut::GLOBAL || type == shortcut::GLOBAL_SET ? ImGuiInputFlags_RouteGlobal
|
||||||
: ImGuiInputFlags_RouteFocused;
|
: ImGuiInputFlags_RouteFocused;
|
||||||
@@ -383,6 +387,15 @@ namespace anm2ed::imgui
|
|||||||
apply();
|
apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiSelectStorage::set_index_map(std::vector<int>* map) { indexMap = map; }
|
||||||
|
|
||||||
|
int MultiSelectStorage::resolve_index(int index) const
|
||||||
|
{
|
||||||
|
if (!indexMap) return index;
|
||||||
|
if (index < 0 || index >= (int)indexMap->size()) return index;
|
||||||
|
return (*indexMap)[index];
|
||||||
|
}
|
||||||
|
|
||||||
PopupHelper::PopupHelper(const char* label, PopupType type, PopupPosition position)
|
PopupHelper::PopupHelper(const char* label, PopupType type, PopupPosition position)
|
||||||
{
|
{
|
||||||
this->label = label;
|
this->label = label;
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ namespace anm2ed::imgui
|
|||||||
public:
|
public:
|
||||||
ImGuiSelectionExternalStorage internal{};
|
ImGuiSelectionExternalStorage internal{};
|
||||||
ImGuiMultiSelectIO* io{};
|
ImGuiMultiSelectIO* io{};
|
||||||
|
std::vector<int>* indexMap{};
|
||||||
|
|
||||||
using std::set<int>::set;
|
using std::set<int>::set;
|
||||||
using std::set<int>::operator=;
|
using std::set<int>::operator=;
|
||||||
@@ -210,6 +211,8 @@ namespace anm2ed::imgui
|
|||||||
ImGuiMultiSelectFlags_ScopeWindow);
|
ImGuiMultiSelectFlags_ScopeWindow);
|
||||||
void apply();
|
void apply();
|
||||||
void finish();
|
void finish();
|
||||||
|
void set_index_map(std::vector<int>*);
|
||||||
|
int resolve_index(int) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PopupHelper
|
class PopupHelper
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
if (ImGui::Begin("Animations", &settings.windowIsAnimations))
|
if (ImGui::Begin("Animations", &settings.windowIsAnimations))
|
||||||
{
|
{
|
||||||
|
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyPressed(ImGuiKey_Escape))
|
||||||
|
reference = {};
|
||||||
|
|
||||||
auto childSize = size_without_footer_get();
|
auto childSize = size_without_footer_get();
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("##Animations Child", childSize, ImGuiChildFlags_Borders))
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ namespace anm2ed::imgui
|
|||||||
bool isTextureValid = spritesheet.texture.is_valid();
|
bool isTextureValid = spritesheet.texture.is_valid();
|
||||||
auto& texture = isTextureValid ? spritesheet.texture : resources.icons[icon::NONE];
|
auto& texture = isTextureValid ? spritesheet.texture : resources.icons[icon::NONE];
|
||||||
auto textureRef = ImTextureRef(texture.id);
|
auto textureRef = ImTextureRef(texture.id);
|
||||||
auto tintColor =
|
auto tintColor = !isTextureValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
!isTextureValid ? ImVec4(1.0f, 0.25f, 0.25f, 1.0f) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
const std::string pathString =
|
const std::string pathString =
|
||||||
spritesheet.path.empty() ? std::string{anm2::NO_PATH} : spritesheet.path.string();
|
spritesheet.path.empty() ? std::string{anm2::NO_PATH} : spritesheet.path.string();
|
||||||
const char* pathCStr = pathString.c_str();
|
const char* pathCStr = pathString.c_str();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ namespace anm2ed::imgui
|
|||||||
constexpr auto FRAME_MULTIPLE = 5;
|
constexpr auto FRAME_MULTIPLE = 5;
|
||||||
constexpr auto FRAME_DRAG_PAYLOAD_ID = "Frame Drag Drop";
|
constexpr auto FRAME_DRAG_PAYLOAD_ID = "Frame Drag Drop";
|
||||||
constexpr auto FRAME_TOOLTIP_HOVER_DELAY = 0.75f; // Extra delay for frame info tooltip.
|
constexpr auto FRAME_TOOLTIP_HOVER_DELAY = 0.75f; // Extra delay for frame info tooltip.
|
||||||
|
constexpr int ITEM_SELECTION_NULL_FLAG = 1 << 30;
|
||||||
|
|
||||||
constexpr auto HELP_FORMAT = R"(- Press {} to decrement time.
|
constexpr auto HELP_FORMAT = R"(- Press {} to decrement time.
|
||||||
- Press {} to increment time.
|
- Press {} to increment time.
|
||||||
@@ -97,7 +99,40 @@ namespace anm2ed::imgui
|
|||||||
auto& playback = document.playback;
|
auto& playback = document.playback;
|
||||||
auto& reference = document.reference;
|
auto& reference = document.reference;
|
||||||
auto& frames = document.frames;
|
auto& frames = document.frames;
|
||||||
|
auto& items = document.items;
|
||||||
|
auto& itemSelection = items.selection;
|
||||||
auto animation = document.animation_get();
|
auto animation = document.animation_get();
|
||||||
|
auto item_selection_encode = [&](anm2::Type type, int id) -> int
|
||||||
|
{
|
||||||
|
if (type == anm2::NULL_) return id | ITEM_SELECTION_NULL_FLAG;
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
auto item_selection_decode = [&](int value) -> int { return value & ~ITEM_SELECTION_NULL_FLAG; };
|
||||||
|
auto item_selection_value_type = [&](int value) -> anm2::Type
|
||||||
|
{
|
||||||
|
return (value & ITEM_SELECTION_NULL_FLAG) ? anm2::NULL_ : anm2::LAYER;
|
||||||
|
};
|
||||||
|
std::vector<int> itemSelectionIndexMap{};
|
||||||
|
std::unordered_map<int, int> layerSelectionIndex{};
|
||||||
|
std::unordered_map<int, int> nullSelectionIndex{};
|
||||||
|
if (animation)
|
||||||
|
{
|
||||||
|
itemSelectionIndexMap.reserve(animation->layerOrder.size() + animation->nullAnimations.size());
|
||||||
|
for (auto id : animation->layerOrder)
|
||||||
|
{
|
||||||
|
layerSelectionIndex[id] = (int)itemSelectionIndexMap.size();
|
||||||
|
itemSelectionIndexMap.push_back(item_selection_encode(anm2::LAYER, id));
|
||||||
|
}
|
||||||
|
for (auto& [id, nullAnimation] : animation->nullAnimations)
|
||||||
|
{
|
||||||
|
(void)nullAnimation;
|
||||||
|
nullSelectionIndex[id] = (int)itemSelectionIndexMap.size();
|
||||||
|
itemSelectionIndexMap.push_back(item_selection_encode(anm2::NULL_, id));
|
||||||
|
}
|
||||||
|
itemSelection.set_index_map(&itemSelectionIndexMap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
itemSelection.set_index_map(nullptr);
|
||||||
|
|
||||||
style = ImGui::GetStyle();
|
style = ImGui::GetStyle();
|
||||||
auto isLightTheme = settings.theme == theme::LIGHT;
|
auto isLightTheme = settings.theme == theme::LIGHT;
|
||||||
@@ -137,6 +172,106 @@ namespace anm2ed::imgui
|
|||||||
return ITEM_COLOR_LIGHT_ACTIVE[type_index(type)];
|
return ITEM_COLOR_LIGHT_ACTIVE[type_index(type)];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
items.hovered = -1;
|
||||||
|
|
||||||
|
auto item_selection_type_get = [&]() -> anm2::Type
|
||||||
|
{
|
||||||
|
if (itemSelection.empty() || !animation) return anm2::NONE;
|
||||||
|
|
||||||
|
for (auto encoded : itemSelection)
|
||||||
|
{
|
||||||
|
auto valueType = item_selection_value_type(encoded);
|
||||||
|
auto valueID = item_selection_decode(encoded);
|
||||||
|
if (valueType == anm2::LAYER && animation->layerAnimations.contains(valueID)) return anm2::LAYER;
|
||||||
|
if (valueType == anm2::NULL_ && animation->nullAnimations.contains(valueID)) return anm2::NULL_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return anm2::NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_selection_clear = [&]()
|
||||||
|
{
|
||||||
|
itemSelection.clear();
|
||||||
|
items.reference = -1;
|
||||||
|
document.layer.selection.clear();
|
||||||
|
document.null.selection.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_selection_sync = [&]()
|
||||||
|
{
|
||||||
|
if (itemSelection.empty())
|
||||||
|
{
|
||||||
|
item_selection_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = item_selection_type_get();
|
||||||
|
items.reference = (int)type;
|
||||||
|
|
||||||
|
auto assign_selection = [&](MultiSelectStorage& target, anm2::Type assignType)
|
||||||
|
{
|
||||||
|
target.clear();
|
||||||
|
for (auto encoded : itemSelection)
|
||||||
|
{
|
||||||
|
if (item_selection_value_type(encoded) != assignType) continue;
|
||||||
|
target.insert(item_selection_decode(encoded));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == anm2::LAYER)
|
||||||
|
assign_selection(document.layer.selection, anm2::LAYER);
|
||||||
|
else if (type == anm2::NULL_)
|
||||||
|
assign_selection(document.null.selection, anm2::NULL_);
|
||||||
|
else
|
||||||
|
item_selection_clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_selection_prune = [&]()
|
||||||
|
{
|
||||||
|
if (itemSelection.empty())
|
||||||
|
{
|
||||||
|
items.reference = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!animation)
|
||||||
|
{
|
||||||
|
item_selection_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = item_selection_type_get();
|
||||||
|
if (type != anm2::LAYER && type != anm2::NULL_)
|
||||||
|
{
|
||||||
|
item_selection_clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = itemSelection.begin(); it != itemSelection.end();)
|
||||||
|
{
|
||||||
|
if (item_selection_value_type(*it) != type)
|
||||||
|
{
|
||||||
|
it = itemSelection.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto valueID = item_selection_decode(*it);
|
||||||
|
bool exists =
|
||||||
|
type == anm2::LAYER ? animation->layerAnimations.contains(valueID) : animation->nullAnimations.contains(valueID);
|
||||||
|
if (!exists)
|
||||||
|
it = itemSelection.erase(it);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemSelection.empty())
|
||||||
|
item_selection_clear();
|
||||||
|
else
|
||||||
|
item_selection_sync();
|
||||||
|
};
|
||||||
|
|
||||||
|
item_selection_prune();
|
||||||
|
|
||||||
auto iconTintDefault = isLightTheme ? ICON_TINT_DEFAULT_LIGHT : ICON_TINT_DEFAULT_DARK;
|
auto iconTintDefault = isLightTheme ? ICON_TINT_DEFAULT_LIGHT : ICON_TINT_DEFAULT_DARK;
|
||||||
auto itemIconTint = isLightTheme ? ICON_TINT_DEFAULT_LIGHT : iconTintDefault;
|
auto itemIconTint = isLightTheme ? ICON_TINT_DEFAULT_LIGHT : iconTintDefault;
|
||||||
auto frameBorderColor = isLightTheme ? FRAME_BORDER_COLOR_LIGHT : FRAME_BORDER_COLOR_DARK;
|
auto frameBorderColor = isLightTheme ? FRAME_BORDER_COLOR_LIGHT : FRAME_BORDER_COLOR_DARK;
|
||||||
@@ -288,9 +423,28 @@ namespace anm2ed::imgui
|
|||||||
addItemName.clear();
|
addItemName.clear();
|
||||||
addItemSpritesheetID = {};
|
addItemSpritesheetID = {};
|
||||||
addItemID = -1;
|
addItemID = -1;
|
||||||
unusedItems = reference.itemType == anm2::LAYER ? anm2.layers_unused(*animation)
|
};
|
||||||
: reference.itemType == anm2::NULL_ ? anm2.nulls_unused(*animation)
|
|
||||||
: std::set<int>{};
|
auto unused_items_get = [&](anm2::Type type)
|
||||||
|
{
|
||||||
|
if (!animation) return std::set<int>{};
|
||||||
|
if (type == anm2::LAYER) return anm2.layers_unused(*animation);
|
||||||
|
if (type == anm2::NULL_) return anm2.nulls_unused(*animation);
|
||||||
|
return std::set<int>{};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_selection_index_get = [&](anm2::Type type, int id)
|
||||||
|
{
|
||||||
|
if (type == anm2::LAYER)
|
||||||
|
{
|
||||||
|
if (auto it = layerSelectionIndex.find(id); it != layerSelectionIndex.end()) return it->second;
|
||||||
|
}
|
||||||
|
else if (type == anm2::NULL_)
|
||||||
|
{
|
||||||
|
if (auto it = nullSelectionIndex.find(id); it != nullSelectionIndex.end()) return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto item_child = [&](anm2::Type type, int id, int& index)
|
auto item_child = [&](anm2::Type type, int id, int& index)
|
||||||
@@ -303,7 +457,7 @@ namespace anm2ed::imgui
|
|||||||
if (isOnlyShowLayers && type != anm2::LAYER) isVisible = false;
|
if (isOnlyShowLayers && type != anm2::LAYER) isVisible = false;
|
||||||
auto isActive = reference.itemType == type && reference.itemID == id;
|
auto isActive = reference.itemType == type && reference.itemID == id;
|
||||||
|
|
||||||
auto label = type == anm2::LAYER ? std::format(anm2::LAYER_FORMAT, id, anm2.content.layers.at(id).name,
|
auto label = type == anm2::LAYER ? std::format(anm2::LAYER_FORMAT, id, anm2.content.layers[id].name,
|
||||||
anm2.content.layers[id].spritesheetID)
|
anm2.content.layers[id].spritesheetID)
|
||||||
: type == anm2::NULL_ ? std::format(anm2::NULL_FORMAT, id, anm2.content.nulls[id].name)
|
: type == anm2::NULL_ ? std::format(anm2::NULL_FORMAT, id, anm2.content.nulls[id].name)
|
||||||
: anm2::TYPE_STRINGS[type];
|
: anm2::TYPE_STRINGS[type];
|
||||||
@@ -311,9 +465,14 @@ namespace anm2ed::imgui
|
|||||||
auto iconTintCurrent = isLightTheme && type == anm2::NONE ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : itemIconTint;
|
auto iconTintCurrent = isLightTheme && type == anm2::NONE ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : itemIconTint;
|
||||||
auto baseColorVec = item_color_vec(type);
|
auto baseColorVec = item_color_vec(type);
|
||||||
auto activeColorVec = item_color_active_vec(type);
|
auto activeColorVec = item_color_active_vec(type);
|
||||||
|
auto selectionType = item_selection_type_get();
|
||||||
|
bool isSelectableItem = type == anm2::LAYER || type == anm2::NULL_;
|
||||||
|
auto selectionIndex = item_selection_index_get(type, id);
|
||||||
|
int selectionValue = item_selection_encode(type, id);
|
||||||
|
bool isMultiSelected = isSelectableItem && selectionType == type && itemSelection.contains(selectionValue);
|
||||||
bool isTypeNone = type == anm2::NONE;
|
bool isTypeNone = type == anm2::NONE;
|
||||||
auto colorVec = baseColorVec;
|
auto colorVec = baseColorVec;
|
||||||
if (isActive && !isTypeNone)
|
if ((isActive || isMultiSelected) && !isTypeNone)
|
||||||
{
|
{
|
||||||
if (isLightTheme)
|
if (isLightTheme)
|
||||||
colorVec = ITEM_COLOR_LIGHT_SELECTED[type_index(type)];
|
colorVec = ITEM_COLOR_LIGHT_SELECTED[type_index(type)];
|
||||||
@@ -344,19 +503,29 @@ namespace anm2ed::imgui
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4());
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4());
|
||||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4());
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4());
|
||||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4());
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4());
|
||||||
if (ImGui::Selectable("##Item Button", false, ImGuiSelectableFlags_SelectOnNav, itemSize))
|
if (isSelectableItem && selectionIndex != -1) ImGui::SetNextItemSelectionUserData(selectionIndex);
|
||||||
|
if (ImGui::Selectable("##Item Button", isSelectableItem && isMultiSelected, ImGuiSelectableFlags_SelectOnNav,
|
||||||
|
itemSize))
|
||||||
{
|
{
|
||||||
if (type == anm2::LAYER)
|
if (isSelectableItem)
|
||||||
{
|
{
|
||||||
document.spritesheet.reference = anm2.content.layers[id].spritesheetID;
|
auto previousType = item_selection_type_get();
|
||||||
document.layer.selection = {id};
|
bool typeMismatch =
|
||||||
|
!itemSelection.empty() && previousType != anm2::NONE && previousType != type;
|
||||||
|
if (typeMismatch)
|
||||||
|
{
|
||||||
|
itemSelection.clear();
|
||||||
|
itemSelection.insert(selectionValue);
|
||||||
}
|
}
|
||||||
else if (type == anm2::NULL_)
|
item_selection_sync();
|
||||||
document.null.selection = {id};
|
}
|
||||||
|
|
||||||
|
if (type == anm2::LAYER) document.spritesheet.reference = anm2.content.layers[id].spritesheetID;
|
||||||
|
|
||||||
reference_set_item(type, id);
|
reference_set_item(type, id);
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor(3);
|
ImGui::PopStyleColor(3);
|
||||||
|
if (ImGui::IsItemHovered()) items.hovered = id;
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
{
|
{
|
||||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||||
@@ -537,6 +706,9 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
if (animation)
|
if (animation)
|
||||||
{
|
{
|
||||||
|
item_selection_prune();
|
||||||
|
itemSelection.start(itemSelectionIndexMap.size(), ImGuiMultiSelectFlags_ClearOnEscape);
|
||||||
|
|
||||||
item_child_row(anm2::ROOT);
|
item_child_row(anm2::ROOT);
|
||||||
|
|
||||||
for (auto& id : animation->layerOrder)
|
for (auto& id : animation->layerOrder)
|
||||||
@@ -552,6 +724,9 @@ namespace anm2ed::imgui
|
|||||||
}
|
}
|
||||||
|
|
||||||
item_child_row(anm2::TRIGGER);
|
item_child_row(anm2::TRIGGER);
|
||||||
|
|
||||||
|
itemSelection.finish();
|
||||||
|
item_selection_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHorizontalScroll && ImGui::GetCurrentWindow()->ScrollbarY)
|
if (isHorizontalScroll && ImGui::GetCurrentWindow()->ScrollbarY)
|
||||||
@@ -583,19 +758,33 @@ namespace anm2ed::imgui
|
|||||||
set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
|
set_item_tooltip_shortcut("Add a new item to the animation.", settings.shortcutAdd);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::BeginDisabled(!document.item_get() && reference.itemType != anm2::LAYER &&
|
auto selectionType = item_selection_type_get();
|
||||||
reference.itemType != anm2::NULL_);
|
bool hasSelection = !itemSelection.empty() && (selectionType == anm2::LAYER || selectionType == anm2::NULL_);
|
||||||
|
bool hasReferenceItem = document.item_get() != nullptr;
|
||||||
|
ImGui::BeginDisabled(!hasSelection && !hasReferenceItem);
|
||||||
{
|
{
|
||||||
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
shortcut(manager.chords[SHORTCUT_REMOVE]);
|
||||||
if (ImGui::Button("Remove", widgetSize))
|
if (ImGui::Button("Remove", widgetSize))
|
||||||
{
|
{
|
||||||
auto remove = [&]()
|
auto remove = [&]()
|
||||||
{
|
{
|
||||||
|
if (hasSelection)
|
||||||
|
{
|
||||||
|
std::vector<int> ids{};
|
||||||
|
ids.reserve(itemSelection.size());
|
||||||
|
for (auto value : itemSelection)
|
||||||
|
ids.push_back(item_selection_decode(value));
|
||||||
|
std::sort(ids.begin(), ids.end());
|
||||||
|
for (auto id : ids)
|
||||||
|
animation->item_remove(selectionType, id);
|
||||||
|
item_selection_clear();
|
||||||
|
}
|
||||||
|
else if (reference.itemType == anm2::LAYER || reference.itemType == anm2::NULL_)
|
||||||
animation->item_remove(reference.itemType, reference.itemID);
|
animation->item_remove(reference.itemType, reference.itemID);
|
||||||
reference_clear();
|
reference_clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
DOCUMENT_EDIT(document, "Remove Item", Document::ITEMS, remove());
|
DOCUMENT_EDIT(document, "Remove Item(s)", Document::ITEMS, remove());
|
||||||
}
|
}
|
||||||
set_item_tooltip_shortcut("Remove the selected item(s) from the animation.", settings.shortcutRemove);
|
set_item_tooltip_shortcut("Remove the selected item(s) from the animation.", settings.shortcutRemove);
|
||||||
}
|
}
|
||||||
@@ -1434,9 +1623,6 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
ImGui::SeparatorText("Source");
|
ImGui::SeparatorText("Source");
|
||||||
|
|
||||||
bool isNewOnly = unusedItems.empty();
|
|
||||||
if (isNewOnly) source = source::NEW;
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("Source New", size))
|
if (ImGui::BeginChild("Source New", size))
|
||||||
{
|
{
|
||||||
ImGui::RadioButton("New", &source, source::NEW);
|
ImGui::RadioButton("New", &source, source::NEW);
|
||||||
@@ -1448,10 +1634,13 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
if (ImGui::BeginChild("Source Existing", size))
|
if (ImGui::BeginChild("Source Existing", size))
|
||||||
{
|
{
|
||||||
ImGui::BeginDisabled(isNewOnly);
|
auto hasUnusedItems = animation && !unused_items_get((anm2::Type)type).empty();
|
||||||
|
ImGui::BeginDisabled(!hasUnusedItems);
|
||||||
ImGui::RadioButton("Existing", &source, source::EXISTING);
|
ImGui::RadioButton("Existing", &source, source::EXISTING);
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
ImGui::SetItemTooltip("Use a pre-existing, presently unused item.");
|
auto tooltip =
|
||||||
|
hasUnusedItems ? "Use a pre-existing, presently unused item." : "No unused items are available.";
|
||||||
|
ImGui::SetItemTooltip("%s", tooltip);
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
@@ -1494,6 +1683,9 @@ namespace anm2ed::imgui
|
|||||||
{
|
{
|
||||||
if (animation && source == source::EXISTING)
|
if (animation && source == source::EXISTING)
|
||||||
{
|
{
|
||||||
|
auto unusedItems = unused_items_get((anm2::Type)type);
|
||||||
|
if (addItemID != -1 && !unusedItems.contains(addItemID)) addItemID = -1;
|
||||||
|
|
||||||
for (auto id : unusedItems)
|
for (auto id : unusedItems)
|
||||||
{
|
{
|
||||||
auto isSelected = addItemID == id;
|
auto isSelected = addItemID == id;
|
||||||
@@ -1522,6 +1714,7 @@ namespace anm2ed::imgui
|
|||||||
|
|
||||||
auto widgetSize = widget_size_with_row_get(2);
|
auto widgetSize = widget_size_with_row_get(2);
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(source == source::EXISTING && addItemID == -1);
|
||||||
if (ImGui::Button("Add", widgetSize))
|
if (ImGui::Button("Add", widgetSize))
|
||||||
{
|
{
|
||||||
anm2::Reference addReference{};
|
anm2::Reference addReference{};
|
||||||
@@ -1537,9 +1730,16 @@ namespace anm2ed::imgui
|
|||||||
document.change(Document::ITEMS);
|
document.change(Document::ITEMS);
|
||||||
|
|
||||||
reference = addReference;
|
reference = addReference;
|
||||||
|
itemSelection.clear();
|
||||||
|
if (addReference.itemType == anm2::LAYER || addReference.itemType == anm2::NULL_)
|
||||||
|
{
|
||||||
|
itemSelection.insert(item_selection_encode(addReference.itemType, addReference.itemID));
|
||||||
|
item_selection_sync();
|
||||||
|
}
|
||||||
|
|
||||||
item_properties_close();
|
item_properties_close();
|
||||||
}
|
}
|
||||||
|
ImGui::EndDisabled();
|
||||||
ImGui::SetItemTooltip("Add the item, with the settings specified.");
|
ImGui::SetItemTooltip("Add the item, with the settings specified.");
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace anm2ed::imgui
|
|||||||
bool isWindowHovered{};
|
bool isWindowHovered{};
|
||||||
bool isHorizontalScroll{};
|
bool isHorizontalScroll{};
|
||||||
PopupHelper propertiesPopup{PopupHelper("Item Properties", POPUP_NORMAL)};
|
PopupHelper propertiesPopup{PopupHelper("Item Properties", POPUP_NORMAL)};
|
||||||
PopupHelper bakePopup{PopupHelper("Bake", POPUP_TO_CONTENT)};
|
PopupHelper bakePopup{PopupHelper("Bake", POPUP_SMALL_NO_HEIGHT)};
|
||||||
std::string addItemName{};
|
std::string addItemName{};
|
||||||
bool addItemIsRect{};
|
bool addItemIsRect{};
|
||||||
int addItemID{-1};
|
int addItemID{-1};
|
||||||
@@ -42,7 +42,6 @@ namespace anm2ed::imgui
|
|||||||
std::vector<int> frameSelectionLocked{};
|
std::vector<int> frameSelectionLocked{};
|
||||||
bool isFrameSelectionLocked{};
|
bool isFrameSelectionLocked{};
|
||||||
anm2::Reference frameSelectionSnapshotReference{};
|
anm2::Reference frameSelectionSnapshotReference{};
|
||||||
std::set<int> unusedItems{};
|
|
||||||
glm::vec2 scroll{};
|
glm::vec2 scroll{};
|
||||||
ImDrawList* pickerLineDrawList{};
|
ImDrawList* pickerLineDrawList{};
|
||||||
ImGuiStyle style{};
|
ImGuiStyle style{};
|
||||||
|
|||||||
Reference in New Issue
Block a user