diff --git a/src/imgui/taskbar.cpp b/src/imgui/taskbar.cpp index e83ce6c..4e91ab7 100644 --- a/src/imgui/taskbar.cpp +++ b/src/imgui/taskbar.cpp @@ -153,8 +153,11 @@ namespace anm2ed::imgui generatePopup.open(); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_WIZARD_GENERATE_ANIMATION_FROM_GRID)); + bool isChangeAllFramesAvailable = + frames && !frames->selection.empty() && document->reference.itemType != anm2::TRIGGER; + bool isChangeAllAnimationsAvailable = document && !document->animation.selection.empty(); if (ImGui::MenuItem(localize.get(LABEL_CHANGE_ALL_FRAME_PROPERTIES), nullptr, false, - frames && !frames->selection.empty() && document->reference.itemType != anm2::TRIGGER)) + isChangeAllFramesAvailable || isChangeAllAnimationsAvailable)) changePopup.open(); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_WIZARD_CHANGE_ALL_FRAME_PROPERTIES)); @@ -232,7 +235,7 @@ namespace anm2ed::imgui { if (document) { - changeAllFrameProperties.update(*document, settings); + changeAllFrameProperties.update(*document, settings, true); if (changeAllFrameProperties.isChanged) changePopup.close(); } ImGui::EndPopup(); diff --git a/src/imgui/wizard/change_all_frame_properties.cpp b/src/imgui/wizard/change_all_frame_properties.cpp index d861db6..e662d22 100644 --- a/src/imgui/wizard/change_all_frame_properties.cpp +++ b/src/imgui/wizard/change_all_frame_properties.cpp @@ -1,6 +1,7 @@ #include "change_all_frame_properties.hpp" #include +#include #include #include @@ -11,11 +12,21 @@ using namespace glm; namespace anm2ed::imgui::wizard { - void ChangeAllFrameProperties::update(Document& document, Settings& settings) + namespace + { + enum ChangeDestination + { + CHANGE_DESTINATION_FRAMES, + CHANGE_DESTINATION_ANIMATIONS, + }; + } + + void ChangeAllFrameProperties::update(Document& document, Settings& settings, bool isFromWizard) { isChanged = false; auto& frames = document.frames.selection; + auto& animations = document.animation.selection; auto& isCropX = settings.changeIsCropX; auto& isCropY = settings.changeIsCropY; auto& isSizeX = settings.changeIsSizeX; @@ -54,8 +65,26 @@ namespace anm2ed::imgui::wizard auto& interpolation = settings.changeInterpolation; auto& isFlipX = settings.changeIsFlipX; auto& isFlipY = settings.changeIsFlipY; + auto& destination = settings.changeDestination; + auto& isRoot = settings.changeIsRoot; + auto& isLayers = settings.changeIsLayers; + auto& isNulls = settings.changeIsNulls; auto& itemType = document.reference.itemType; + bool isFramesDestination = !isFromWizard || destination == CHANGE_DESTINATION_FRAMES; + bool isSelectedFramesAvailable = !frames.empty() && itemType != anm2::TRIGGER; + bool isSelectedAnimationsAvailable = !animations.empty(); + if (isFromWizard) + { + if (destination == CHANGE_DESTINATION_FRAMES && !isSelectedFramesAvailable && isSelectedAnimationsAvailable) + destination = CHANGE_DESTINATION_ANIMATIONS; + if (destination == CHANGE_DESTINATION_ANIMATIONS && !isSelectedAnimationsAvailable && isSelectedFramesAvailable) + destination = CHANGE_DESTINATION_FRAMES; + isFramesDestination = destination == CHANGE_DESTINATION_FRAMES; + } + + bool isLayerPropertyAvailable = isFramesDestination ? itemType == anm2::LAYER : isLayers; + #define PROPERTIES_WIDGET(body, checkboxLabel, isEnabled) \ ImGui::Checkbox(checkboxLabel, &isEnabled); \ ImGui::SameLine(); \ @@ -190,7 +219,7 @@ namespace anm2ed::imgui::wizard ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImGui::GetStyle().ItemInnerSpacing); - ImGui::BeginDisabled(itemType != anm2::LAYER); + ImGui::BeginDisabled(!isLayerPropertyAvailable); float2_value("##Is Crop X", "##Is Crop Y", "##Crop X", localize.get(BASIC_CROP), isCropX, isCropY, crop); float2_value("##Is Size X", "##Is Size Y", "##Size X", localize.get(BASIC_SIZE), isSizeX, isSizeY, size); ImGui::EndDisabled(); @@ -198,7 +227,7 @@ namespace anm2ed::imgui::wizard float2_value("##Is Position X", "##Is Position Y", "##Position X", localize.get(BASIC_POSITION), isPositionX, isPositionY, position); - ImGui::BeginDisabled(itemType != anm2::LAYER); + ImGui::BeginDisabled(!isLayerPropertyAvailable); float2_value("##Is Pivot X", "##Is Pivot Y", "##Pivot X", localize.get(BASIC_PIVOT), isPivotX, isPivotY, pivot); ImGui::EndDisabled(); @@ -215,7 +244,7 @@ namespace anm2ed::imgui::wizard "##Color Offset B", "##Color Offset G", localize.get(BASIC_COLOR_OFFSET), isColorOffsetR, isColorOffsetG, isColorOffsetB, colorOffset); - ImGui::BeginDisabled(itemType != anm2::LAYER); + ImGui::BeginDisabled(!isLayerPropertyAvailable); std::vector fallbackIds{-1}; std::vector fallbackLabelsString{localize.get(BASIC_NONE)}; std::vector fallbackLabels{fallbackLabelsString[0].c_str()}; @@ -244,7 +273,8 @@ namespace anm2ed::imgui::wizard } auto regionIds = regionStorage && !regionStorage->ids.empty() ? regionStorage->ids : fallbackIds; auto regionLabels = regionStorage && !regionStorage->labels.empty() ? regionStorage->labels : fallbackLabels; - if (itemType != anm2::LAYER || std::find(regionIds.begin(), regionIds.end(), regionId) == regionIds.end()) regionId = -1; + if (!isLayerPropertyAvailable || std::find(regionIds.begin(), regionIds.end(), regionId) == regionIds.end()) + regionId = -1; PROPERTIES_WIDGET(combo_id_mapped(localize.get(BASIC_REGION), ®ionId, regionIds, regionLabels), "##Is Region", isRegion); ImGui::EndDisabled(); @@ -293,25 +323,106 @@ namespace anm2ed::imgui::wizard if (isFlipXSet) frameChange.isFlipX = std::make_optional(isFlipX); if (isFlipYSet) frameChange.isFlipY = std::make_optional(isFlipY); - if (auto item = document.item_get()) + auto all_frames_selection = [](anm2::Item& item) { - DOCUMENT_EDIT(document, localize.get(EDIT_CHANGE_FRAME_PROPERTIES), Document::FRAMES, - item->frames_change(frameChange, itemType, changeType, frames)); + std::set selection{}; + for (int i = 0; i < (int)item.frames.size(); ++i) + selection.insert(i); + return selection; + }; + if (isFramesDestination) + { + if (auto item = document.item_get()) + { + DOCUMENT_EDIT(document, localize.get(EDIT_CHANGE_FRAME_PROPERTIES), Document::FRAMES, + item->frames_change(frameChange, itemType, changeType, frames)); + + isChanged = true; + } + } + else + { + auto behavior = [&]() + { + for (auto animationIndex : animations) + { + if (animationIndex < 0 || animationIndex >= (int)document.anm2.animations.items.size()) continue; + auto& animation = document.anm2.animations.items[animationIndex]; + + if (isRoot) + { + auto selection = all_frames_selection(animation.rootAnimation); + animation.rootAnimation.frames_change(frameChange, anm2::ROOT, changeType, selection); + } + + if (isLayers) + { + for (auto& [_, item] : animation.layerAnimations) + { + auto selection = all_frames_selection(item); + item.frames_change(frameChange, anm2::LAYER, changeType, selection); + } + } + + if (isNulls) + { + for (auto& [_, item] : animation.nullAnimations) + { + auto selection = all_frames_selection(item); + item.frames_change(frameChange, anm2::NULL_, changeType, selection); + } + } + } + }; + + DOCUMENT_EDIT(document, localize.get(EDIT_CHANGE_FRAME_PROPERTIES), Document::FRAMES, behavior()); isChanged = true; } }; ImGui::Separator(); + if (isFromWizard) + { + ImGui::SeparatorText(localize.get(LABEL_DESTINATION)); + + ImGui::BeginDisabled(!isSelectedFramesAvailable); + ImGui::RadioButton(localize.get(BASIC_FRAMES), &destination, CHANGE_DESTINATION_FRAMES); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CHANGE_ALL_DESTINATION_FRAMES)); + ImGui::EndDisabled(); + + ImGui::SameLine(); + + ImGui::BeginDisabled(!isSelectedAnimationsAvailable); + ImGui::RadioButton(localize.get(LABEL_ANIMATIONS_CHILD), &destination, CHANGE_DESTINATION_ANIMATIONS); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CHANGE_ALL_DESTINATION_ANIMATIONS)); + ImGui::EndDisabled(); + + ImGui::BeginDisabled(isFramesDestination); + ImGui::Checkbox(localize.get(LABEL_ROOT), &isRoot); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CHANGE_ALL_ROOT)); + ImGui::SameLine(); + ImGui::Checkbox(localize.get(LABEL_LAYERS), &isLayers); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CHANGE_ALL_LAYERS)); + ImGui::SameLine(); + ImGui::Checkbox(localize.get(LABEL_NULLS), &isNulls); + ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_CHANGE_ALL_NULLS)); + ImGui::EndDisabled(); + + ImGui::Separator(); + } + bool isAnyProperty = isCropX || isCropY || isSizeX || isSizeY || isPositionX || isPositionY || isPivotX || isPivotY || isScaleX || isScaleY || isRotation || isDuration || isTintR || isTintG || isTintB || isTintA || isColorOffsetR || isColorOffsetG || isColorOffsetB || isRegion || isVisibleSet || isInterpolationSet || isFlipXSet || isFlipYSet; + bool isDestinationValid = isFramesDestination ? isSelectedFramesAvailable + : isSelectedAnimationsAvailable && (isRoot || isLayers || isNulls); auto rowWidgetSize = widget_size_with_row_get(5); - ImGui::BeginDisabled(!isAnyProperty); + ImGui::BeginDisabled(!isAnyProperty || !isDestinationValid); if (ImGui::Button(localize.get(LABEL_ADJUST), rowWidgetSize)) frame_change(anm2::ADJUST); ImGui::SetItemTooltip("%s", localize.get(TOOLTIP_ADJUST)); diff --git a/src/imgui/wizard/change_all_frame_properties.hpp b/src/imgui/wizard/change_all_frame_properties.hpp index 555472a..3d7373c 100644 --- a/src/imgui/wizard/change_all_frame_properties.hpp +++ b/src/imgui/wizard/change_all_frame_properties.hpp @@ -10,6 +10,6 @@ namespace anm2ed::imgui::wizard public: bool isChanged{}; - void update(Document&, Settings&); + void update(Document&, Settings&, bool = false); }; } diff --git a/src/resource/strings.hpp b/src/resource/strings.hpp index baeb289..c06bd84 100644 --- a/src/resource/strings.hpp +++ b/src/resource/strings.hpp @@ -311,6 +311,9 @@ namespace anm2ed X(LABEL_REGION_ORIGIN_TOP_LEFT, "Top Left", "Superior izquierda", "Верхний левый", "左上", "왼쪽 위") \ X(LABEL_REGION_ORIGIN_CENTER, "Center", "Centro", "Центр", "中心", "중앙") \ X(LABEL_REGION_ORIGIN_CUSTOM, "Custom", "Personalizado", "Пользовательский", "自定义", "사용자 지정") \ + X(LABEL_ROOT, "Root", "Root", "Корень", "根", "Root") \ + X(LABEL_LAYERS, "Layers", "Capas", "Слои", "动画层", "레이어") \ + X(LABEL_NULLS, "Nulls", "Nulls", "Нули", "Nulls", "Null") \ X(LABEL_MANAGER_LAYER_PROPERTIES, "Layer Properties", "Propiedades de Capa", "Свойства слоя", "动画层属性", "레이어 속성") \ X(LABEL_MANAGER_NULL_PROPERTIES, "Null Properties", "Propiedades Null", "Свойства нуля", "Null属性", "Null 속성") \ X(LABEL_MANAGER_RENDERING_PROGRESS, "Rendering...", "Renderizando...", "Рендеринг...", "渲染中...", "렌더링 중...") \ @@ -531,6 +534,11 @@ namespace anm2ed X(TOOLTIP_BORDER, "Toggle the visibility of borders around layers.", "Alterna la visibilidad de los bordes alrededor de las capas.", "Переключить видимость границ около слоев.", "切换动画层边框是否可见.", "레이어 주변 경계선을 표시하거나 숨깁니다.") \ X(TOOLTIP_CANCEL_ADD_ITEM, "Cancel adding an item.", "Cancelar añadir un item.", "Отменить добавление предмета.", "取消添加物品.", "항목 추가를 취소합니다.") \ X(TOOLTIP_CANCEL_BAKE_FRAMES, "Cancel baking frames.", "Cancelar hacer bake de Frames.", "Отменить запечку кадров.", "取消提前渲染.", "프레임 베이킹을 취소합니다.") \ + X(TOOLTIP_CHANGE_ALL_DESTINATION_FRAMES, "Apply specified frame properties to selected frames.", "Aplica las propiedades de frame especificadas a los frames seleccionados.", "Применить указанные свойства кадра к выбранным кадрам.", "将指定的帧属性应用到所选帧。", "지정한 프레임 속성을 선택한 프레임에 적용합니다.") \ + X(TOOLTIP_CHANGE_ALL_DESTINATION_ANIMATIONS, "Apply specified frame properties to the specified items in the selected animations.", "Aplica las propiedades de frame especificadas a los items indicados en las animaciones seleccionadas.", "Применить указанные свойства кадра к указанным элементам в выбранных анимациях.", "将指定的帧属性应用到所选动画中的指定项目。", "지정한 프레임 속성을 선택한 애니메이션의 지정 항목에 적용합니다.") \ + X(TOOLTIP_CHANGE_ALL_ROOT, "The frame property changes will apply to root frames.", "Los cambios de propiedades de frame se aplicaran a los frames root.", "Изменения свойств кадра будут применены к корневым кадрам.", "帧属性更改将应用于根帧。", "프레임 속성 변경 사항을 Root 프레임에 적용합니다.") \ + X(TOOLTIP_CHANGE_ALL_LAYERS, "The frame property changes will apply to layer frames.", "Los cambios de propiedades de frame se aplicaran a los frames de capa.", "Изменения свойств кадра будут применены к кадрам слоев.", "帧属性更改将应用于图层帧。", "프레임 속성 변경 사항을 레이어 프레임에 적용합니다.") \ + X(TOOLTIP_CHANGE_ALL_NULLS, "The frame property changes will apply to null frames.", "Los cambios de propiedades de frame se aplicaran a los frames null.", "Изменения свойств кадра будут применены к нулевым кадрам.", "帧属性更改将应用于 Null 帧。", "프레임 속성 변경 사항을 Null 프레임에 적용합니다.") \ X(TOOLTIP_CENTER_VIEW, "Centers the view.", "Centra la vista.", "Центрирует вид.", "居中视角.", "미리보기 화면을 가운데에 맞춥니다.") \ X(TOOLTIP_CLOSE_SETTINGS, "Close without updating settings.", "Cerrar sin actualizar las configuraciones.", "Закрыть без обновления настройки.", "关闭但不保存设置.", "설정을 갱신하지 않고 닫습니다.") \ X(TOOLTIP_COMPATIBILITY_ISAAC, "Sets the output file format to that of The Binding of Isaac: Rebirth's.\nThis removes the following:\n- Sounds\n- Regions\n- Special interpolation modes\nNOTE: This will not serialize this data and it won't be able to be recovered.", "Establece el formato del archivo de salida al de The Binding of Isaac: Rebirth.\nEsto elimina lo siguiente:\n- Sonidos\n- Regiones\n- Modos especiales de interpolación\nNOTA: Estos datos no se serializaran y no podran recuperarse.", "Устанавливает формат выходного файла как у The Binding of Isaac: Rebirth.\nЭто удаляет следующее:\n- Звуки\n- Регионы\n- Специальные режимы интерполяции\nПРИМЕЧАНИЕ: Эти данные не будут сериализованы, и их нельзя будет восстановить.", "将输出文件格式设置为 The Binding of Isaac: Rebirth 的格式。\n这会移除以下内容:\n- 声音\n- 区域\n- 特殊插值模式\n注意:这些数据不会被序列化,且无法恢复。", "출력 파일 형식을 The Binding of Isaac: Rebirth의 형식으로 설정합니다.\n다음 항목이 제거됩니다:\n- 사운드\n- 영역\n- 특수 보간 모드\n참고: 이 데이터는 직렬화되지 않으며 복구할 수 없습니다.") \ diff --git a/src/settings.hpp b/src/settings.hpp index 1f47fec..cff3c2b 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -115,6 +115,10 @@ namespace anm2ed X(CHANGE_INTERPOLATION, changeInterpolation, STRING_UNDEFINED, INT, 0) \ X(CHANGE_IS_FLIP_X, changeIsFlipX, STRING_UNDEFINED, BOOL, false) \ X(CHANGE_IS_FLIP_Y, changeIsFlipY, STRING_UNDEFINED, BOOL, false) \ + X(CHANGE_DESTINATION, changeDestination, STRING_UNDEFINED, INT, 0) \ + X(CHANGE_IS_ROOT, changeIsRoot, STRING_UNDEFINED, BOOL, true) \ + X(CHANGE_IS_LAYERS, changeIsLayers, STRING_UNDEFINED, BOOL, true) \ + X(CHANGE_IS_NULLS, changeIsNulls, STRING_UNDEFINED, BOOL, true) \ \ X(SCALE_VALUE, scaleValue, STRING_UNDEFINED, FLOAT, 1.0f) \ \