#include "DrawingProgramLayerManagerGUI.hpp" #include "DrawingProgramLayerListItem.hpp" #include "DrawingProgramLayerManager.hpp" #include "../DrawingProgram.hpp" #include "../../World.hpp" #include "../../MainProgram.hpp" #include "Helpers/ConvertVec.hpp" #include "Helpers/NetworkingObjects/NetObjOrderedList.hpp" #include "SerializedBlendMode.hpp" DrawingProgramLayerManagerGUI::DrawingProgramLayerManagerGUI(DrawingProgramLayerManager& drawLayerMan): layerMan(drawLayerMan) {} void DrawingProgramLayerManagerGUI::refresh_gui_data() { selectionData = GUIStuff::TreeListing::SelectionData(); editing_layer_check(); oldSelection.clear(); nameToEdit.clear(); nameForNew.clear(); alphaValToEdit = 0.0f; blendModeValToEdit = 0; } void DrawingProgramLayerManagerGUI::setup_list_gui(const char* id, bool& hoveringOverDropdown) { using namespace NetworkingObjects; auto& world = layerMan.drawP.world; if(layerMan.layerTreeRoot) { auto& gui = world.main.toolbar.gui; gui.push_id(id); if(layerMan.layerTreeRoot->get_folder().folderList->empty()) { gui.text_label_centered("No layers exist."); selectionData = GUIStuff::TreeListing::SelectionData(); oldSelection.clear(); nameToEdit.clear(); } else { std::optional toDeleteParent; std::optional toDeleteObject; gui.tree_listing("list", layerMan.layerTreeRoot.get_net_id(), GUIStuff::TreeListing::DisplayData{ .getObjInListAtIndex = [&](NetObjID parentId, size_t index) -> std::optional { NetObjOrderedList& parentFolder = *world.netObjMan.get_obj_temporary_ref_from_id(parentId)->get_folder().folderList; if(parentFolder.size() <= index) return std::nullopt; return GUIStuff::TreeListing::DisplayData::ObjInList{ .id = parentFolder.at(index)->obj.get_net_id(), .isDirectory = parentFolder.at(index)->obj->is_folder(), .isDirectoryOpen = parentFolder.at(index)->obj->is_folder() ? parentFolder.at(index)->obj->get_folder().isFolderOpen : false }; }, .getIndexOfObjInList = [&](const GUIStuff::TreeListing::ParentObjectIDPair& idPair) { return world.netObjMan.get_obj_temporary_ref_from_id(idPair.parent)->get_folder().folderList->get(idPair.object)->pos; }, .setDirectoryOpen = [&](NetObjID netID, bool newDirectoryOpen) { world.netObjMan.get_obj_temporary_ref_from_id(netID)->get_folder().isFolderOpen = newDirectoryOpen; }, .drawNonDirectoryObjIconGUI = [&](NetObjID netID) { CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(2), }, }) { gui.svg_icon("ico", "data/icons/layer.svg"); } return false; }, .drawObjGUI = [&](const GUIStuff::TreeListing::ParentObjectIDPair& idPair) { auto tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(idPair.object); bool isButtonClicked = false; if(Clay_Hovered() && gui.io->mouse.leftClick >= 2 && !tempPtr->is_folder() && selectionData.objsSelected.contains(idPair.object) && !gui.io->key.leftCtrl && !gui.io->key.leftShift) layerMan.editingLayer = tempPtr; CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_FIXED(GUIStuff::TreeListing::ENTRY_HEIGHT), .height = CLAY_SIZING_FIXED(GUIStuff::TreeListing::ENTRY_HEIGHT)}, .padding = CLAY_PADDING_ALL(2), }, }) { if(tempPtr.get_net_id() == layerMan.editingLayer.get_net_id()) gui.svg_icon("edit ico", "data/icons/pencil.svg"); } CLAY_AUTO_ID({ .layout = { .sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}, .childAlignment = {.x = CLAY_ALIGN_X_LEFT, .y = CLAY_ALIGN_Y_CENTER} }, }) { gui.text_label(tempPtr->get_name()); } if(gui.svg_icon_button_transparent("edit button", "data/icons/pencil.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { layerMan.editingLayer = tempPtr; isButtonClicked = true; } if(gui.svg_icon_button_transparent("visible button", tempPtr->get_visible() ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { tempPtr->set_visible(layerMan, !tempPtr->get_visible()); isButtonClicked = true; } if(gui.svg_icon_button_transparent("delete button", "data/icons/trash.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { toDeleteParent = idPair.parent; toDeleteObject = idPair.object; isButtonClicked = true; } return isButtonClicked; }, .moveObjectsToListAtIndex = [&](NetObjID listObj, size_t index, const std::vector& objsToInsert) { std::unordered_map, std::vector>> toEraseMapUndo; WorldUndoManager::UndoObjectID insertParentUndoID; uint32_t insertUndoPosition; std::vector insertedUndoIDs; world.netObjMan.send_multi_update_messsage([&]() { std::unordered_map>> toEraseMap; uint32_t newIndex = index; std::vector> toInsertObjPtrs; auto& listPtr = world.netObjMan.get_obj_temporary_ref_from_id(listObj)->get_folder().folderList; // Map parent folders to list of objects that should be erased from them for(size_t i = 0; i < objsToInsert.size(); i++) { auto& parentListPtr = world.netObjMan.get_obj_temporary_ref_from_id(objsToInsert[i].parent)->get_folder().folderList; toEraseMap[objsToInsert[i].parent].emplace_back(parentListPtr->get(objsToInsert[i].object)); if(newIndex != 0 && objsToInsert[i].parent == listObj && listPtr->get(objsToInsert[i].object)->pos <= index) newIndex--; } // Prepare erase map for undo for(auto& [parentNetID, objsToEraseList] : toEraseMap) { auto& undoEraseListPairForParent = toEraseMapUndo[world.undo.get_undoid_from_netid(parentNetID)]; uint32_t i = 0; for(auto& objToErase : objsToEraseList) { undoEraseListPairForParent.first.emplace_back(objToErase->pos - i); undoEraseListPairForParent.second.emplace_back(world.undo.get_undoid_from_netid(objToErase->obj.get_net_id())); i++; } } // Erase the list of objects we previously mapped to each parent, and move the erased objects to vector toInsertObjPtrs for(auto& [listToEraseFrom, setToErase] : toEraseMap) { auto& listToEraseFromPtr = world.netObjMan.get_obj_temporary_ref_from_id(listToEraseFrom)->get_folder().folderList; listToEraseFromPtr->erase_list(listToEraseFromPtr, setToErase, &toInsertObjPtrs); } // Insert objects in toInsertObjPtrs into the folder we have selected std::vector, NetObjOwnerPtr>> toInsert; auto insertIt = listPtr->at(newIndex); for(uint32_t i = 0; i < objsToInsert.size(); i++) { auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [id = objsToInsert[i].object](NetObjOwnerPtr& objPtr) { return objPtr.get_net_id() == id; }); toInsert.emplace_back(insertIt, std::move(*it)); toInsert.back().second.reassign_ids(); } std::vector> insertedIterators = listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); // Prepare insert list for undo insertParentUndoID = world.undo.get_undoid_from_netid(listObj); insertUndoPosition = newIndex; for(auto& it : insertedIterators) insertedUndoIDs.emplace_back(world.undo.get_undoid_from_netid(it->obj.get_net_id())); }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); class MoveLayersWorldUndoAction : public WorldUndoAction { public: // If the undo ID maps to the net id, we can also assume that the object is definitely inside the list we want to move it from, and not inside some other list // If it was moved to another list, there would be an undo move that would move it back to this list, and if it was moved by another player the undo id wouldnt map back to the net id, and the undo would fail // We can also assume all objects in all lists remain in the same order for the same reason, so no need to sort anything if it was sorted when the undo action was generated MoveLayersWorldUndoAction(const std::unordered_map, std::vector>>& initUndoEraseMap, WorldUndoManager::UndoObjectID initInsertParentUndoID, uint32_t initInsertPosition, std::vector initInsertedUndoIDs): undoEraseMap(initUndoEraseMap), insertParentUndoID(initInsertParentUndoID), insertPosition(initInsertPosition), insertedUndoIDs(initInsertedUndoIDs) {} std::string get_name() const override { return "Move Layers"; } bool undo(WorldUndoManager& undoMan) override { std::vector> toMoveObjs; std::vector> toEraseList; NetObjTemporaryPtr> eraseListPtr; std::unordered_map*, std::vector>> toInsertToMap; { std::optional eraseListNetID = undoMan.get_netid_from_undoid(insertParentUndoID); if(!eraseListNetID.has_value()) return false; eraseListPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(eraseListNetID.value())->get_folder().folderList; } { std::vector toEraseNetIDList; if(!undoMan.fill_netid_list_from_undoid_list(toEraseNetIDList, insertedUndoIDs)) return false; toEraseList = eraseListPtr->get_list(toEraseNetIDList); } { for(auto& [undoID, indexUndoIdListPair] : undoEraseMap) { std::optional netID = undoMan.get_netid_from_undoid(undoID); if(!netID.has_value()) return false; auto& insertMapEntry = toInsertToMap[netID.value()]; insertMapEntry.first = &indexUndoIdListPair.first; if(!undoMan.fill_netid_list_from_undoid_list(insertMapEntry.second, indexUndoIdListPair.second)) return false; } } undoMan.world.netObjMan.send_multi_update_messsage([&]() { eraseListPtr->erase_list(eraseListPtr, toEraseList, &toMoveObjs); for(auto& [parentNetID, indexNetIDListPair] : toInsertToMap) { auto& parentListPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(parentNetID)->get_folder().folderList; std::vector> insertIteratorList = parentListPtr->at_ordered_indices(*indexNetIDListPair.first); auto& insertNetIDList = indexNetIDListPair.second; std::vector, NetObjOwnerPtr>> toInsert; for(auto [insertIt, netID] : std::views::zip(insertIteratorList, insertNetIDList)) { auto it = std::find_if(toMoveObjs.begin(), toMoveObjs.end(), [id = netID](NetObjOwnerPtr& objPtr) { return objPtr.get_net_id() == id; }); toInsert.emplace_back(insertIt, std::move(*it)); toInsert.back().second.reassign_ids(); } parentListPtr->insert_ordered_list_and_send_create(parentListPtr, toInsert); } }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); return true; } bool redo(WorldUndoManager& undoMan) override { std::unordered_map> toEraseMap; NetObjTemporaryPtr> listPtr; std::vector objsToInsert; { for(auto& [undoID, indexUndoIDListPair] : undoEraseMap) { std::optional netID = undoMan.get_netid_from_undoid(undoID); if(!netID.has_value()) return false; if(!undoMan.fill_netid_list_from_undoid_list(toEraseMap[netID.value()], indexUndoIDListPair.second)) return false; } } { std::optional netID = undoMan.get_netid_from_undoid(insertParentUndoID); if(!netID.has_value()) return false; listPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(netID.value())->get_folder().folderList; } { if(!undoMan.fill_netid_list_from_undoid_list(objsToInsert, insertedUndoIDs)) return false; } undoMan.world.netObjMan.send_multi_update_messsage([&]() { std::vector> toInsertObjPtrs; for(auto& [listToEraseFrom, setToErase] : toEraseMap) { auto& listToEraseFromPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(listToEraseFrom)->get_folder().folderList; listToEraseFromPtr->erase_list(listToEraseFromPtr, listToEraseFromPtr->get_list(setToErase), &toInsertObjPtrs); } std::vector, NetObjOwnerPtr>> toInsert; auto insertIt = listPtr->at(insertPosition); for(uint32_t i = 0; i < objsToInsert.size(); i++) { auto it = std::find_if(toInsertObjPtrs.begin(), toInsertObjPtrs.end(), [id = objsToInsert[i]](NetObjOwnerPtr& objPtr) { return objPtr.get_net_id() == id; }); toInsert.emplace_back(insertIt, std::move(*it)); toInsert.back().second.reassign_ids(); } listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); return true; } ~MoveLayersWorldUndoAction() {} std::unordered_map, std::vector>> undoEraseMap; WorldUndoManager::UndoObjectID insertParentUndoID; uint32_t insertPosition; std::vector insertedUndoIDs; }; world.undo.push(std::make_unique(toEraseMapUndo, insertParentUndoID, insertUndoPosition, insertedUndoIDs)); } }, selectionData); if(selectionData.objsSelected != oldSelection) { editing_layer_check(); oldSelection = selectionData.objsSelected; if(selectionData.objsSelected.size() == 1) { NetObjTemporaryPtr tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(*selectionData.objsSelected.begin()); if(tempPtr) { nameToEdit = tempPtr->get_name(); alphaValToEdit = tempPtr->get_alpha(); auto it = std::find(get_blend_mode_useful_list().begin(), get_blend_mode_useful_list().end(), tempPtr->get_blend_mode()); blendModeValToEdit = (it == get_blend_mode_useful_list().end()) ? 0 : (it - get_blend_mode_useful_list().begin()); } else { nameToEdit.clear(); alphaValToEdit = 0.0f; blendModeValToEdit = 0; } } else { nameToEdit.clear(); alphaValToEdit = 0.0f; blendModeValToEdit = 0; } } if(toDeleteObject.has_value()) remove_layer(toDeleteParent.value(), toDeleteObject.value()); } gui.left_to_right_line_layout([&]() { bool addByEnter = false; gui.input_text("input new name", &nameForNew, true, [&](GUIStuff::SelectionHelper& s) { addByEnter = s.selected && gui.io->key.enter; }); if(gui.svg_icon_button("create new layer", "data/icons/plusbold.svg", false, gui.SMALL_BUTTON_SIZE) || addByEnter) { auto newLayerObjInfo = create_layer(new DrawingProgramLayerListItem(world.netObjMan, nameForNew, false)); layerMan.editingLayer = newLayerObjInfo->obj; } else if(gui.svg_icon_button("create new folder", "data/icons/folderbold.svg", false, gui.SMALL_BUTTON_SIZE)) { auto newFolderObjInfo = create_layer(new DrawingProgramLayerListItem(world.netObjMan, nameForNew, true)); newFolderObjInfo->obj->get_folder().isFolderOpen = true; } }); if(selectionData.objsSelected.size() == 1) { NetObjTemporaryPtr tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(*selectionData.objsSelected.begin()); if(tempPtr) { gui.text_label_centered(tempPtr->is_folder() ? "Edit Layer Folder" : "Edit Layer"); gui.input_text_field("input edit name", "Name", &nameToEdit); tempPtr->set_name(world.delayedUpdateObjectManager, nameToEdit); gui.slider_scalar_field("input alpha slider", "Alpha", &alphaValToEdit, 0.0f, 1.0f, 2); tempPtr->set_alpha(layerMan, alphaValToEdit); gui.left_to_right_line_layout([&]() { gui.text_label("Blend Mode"); gui.dropdown_select("input blend mode", &blendModeValToEdit, get_blend_mode_useful_name_list(), 240.0f, [&hoveringOverDropdown]() { if(Clay_Hovered()) hoveringOverDropdown = true; }); }); tempPtr->set_blend_mode(layerMan, get_blend_mode_useful_list()[blendModeValToEdit]); } } gui.pop_id(); } } std::optional>> DrawingProgramLayerManagerGUI::try_to_create_in_proper_position(DrawingProgramLayerListItem* newItem) { using namespace NetworkingObjects; auto& world = layerMan.drawP.world; if(selectionData.orderedObjsSelected.empty()) return std::nullopt; auto& backObjParentObjectPair = selectionData.orderedObjsSelected.back(); NetObjTemporaryPtr parentPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.parent); if(!parentPtr) return std::nullopt; NetObjTemporaryPtr objectPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.object); if(!objectPtr) return std::nullopt; if(objectPtr->is_folder()) { objectPtr->get_folder().isFolderOpen = true; return std::pair>{objectPtr.get_net_id(), objectPtr->get_folder().folderList->insert_and_send_create(objectPtr->get_folder().folderList, objectPtr->get_folder().folderList->begin(), newItem)}; } return std::pair>{parentPtr.get_net_id(), parentPtr->get_folder().folderList->insert_and_send_create(parentPtr->get_folder().folderList, parentPtr->get_folder().folderList->get(objectPtr.get_net_id()), newItem)}; } std::pair> DrawingProgramLayerManagerGUI::create_in_proper_position(DrawingProgramLayerListItem* newItem) { auto toRet = try_to_create_in_proper_position(newItem); if(!toRet) return {layerMan.layerTreeRoot.get_net_id(), layerMan.layerTreeRoot->get_folder().folderList->insert_and_send_create(layerMan.layerTreeRoot->get_folder().folderList, layerMan.layerTreeRoot->get_folder().folderList->begin(), newItem)}; return toRet.value(); } NetworkingObjects::NetObjOrderedListIterator DrawingProgramLayerManagerGUI::create_layer(DrawingProgramLayerListItem* newItem) { auto& world = layerMan.drawP.world; class AddLayerWorldUndoAction : public WorldUndoAction { public: AddLayerWorldUndoAction(std::unique_ptr initLayerData, uint32_t newPos, WorldUndoManager::UndoObjectID initParentUndoID, WorldUndoManager::UndoObjectID initUndoID): layerData(std::move(initLayerData)), pos(newPos), parentUndoID(initParentUndoID), undoID(initUndoID) {} std::string get_name() const override { return "Add Layer Item"; } bool undo(WorldUndoManager& undoMan) override { std::optional toEraseParentID = undoMan.get_netid_from_undoid(parentUndoID); if(!toEraseParentID.has_value()) return false; std::optional toEraseID = undoMan.get_netid_from_undoid(undoID); if(!toEraseID.has_value()) return false; auto& layerList = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(toEraseParentID.value())->get_folder().folderList; auto it = layerList->get(toEraseID.value()); layerData = std::make_unique(it->obj->get_undo_data(undoMan)); layerList->erase(layerList, it); return true; } bool redo(WorldUndoManager& undoMan) override { std::optional toInsertParentID = undoMan.get_netid_from_undoid(parentUndoID); if(!toInsertParentID.has_value()) return false; std::optional toInsertID = undoMan.get_netid_from_undoid(undoID); if(toInsertID.has_value()) return false; auto& layerList = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(toInsertParentID.value())->get_folder().folderList; auto insertedIt = layerList->emplace_direct(layerList, layerList->at(pos), undoMan.world, *layerData); undoMan.register_new_netid_to_existing_undoid(undoID, insertedIt->obj.get_net_id()); layerData = nullptr; return true; } void scale_up(const WorldScalar& scaleAmount) override { if(layerData) layerData->scale_up(scaleAmount); } ~AddLayerWorldUndoAction() {} std::unique_ptr layerData; uint32_t pos; WorldUndoManager::UndoObjectID parentUndoID; WorldUndoManager::UndoObjectID undoID; }; auto insertedLayerPair = create_in_proper_position(newItem); auto& it = insertedLayerPair.second; world.undo.push(std::make_unique(std::make_unique(it->obj->get_undo_data(world.undo)), it->pos, world.undo.get_undoid_from_netid(insertedLayerPair.first), world.undo.get_undoid_from_netid(it->obj.get_net_id()))); return it; } void DrawingProgramLayerManagerGUI::remove_layer(const NetworkingObjects::NetObjID& parentID, const NetworkingObjects::NetObjID& objectID) { auto& world = layerMan.drawP.world; class DeleteLayerWorldUndoAction : public WorldUndoAction { public: DeleteLayerWorldUndoAction(std::unique_ptr initLayerData, uint32_t newPos, WorldUndoManager::UndoObjectID initParentUndoID, WorldUndoManager::UndoObjectID initUndoID): layerData(std::move(initLayerData)), pos(newPos), parentUndoID(initParentUndoID), undoID(initUndoID) {} std::string get_name() const override { return "Delete Layer Item"; } bool undo(WorldUndoManager& undoMan) override { std::optional toInsertParentID = undoMan.get_netid_from_undoid(parentUndoID); if(!toInsertParentID.has_value()) return false; std::optional toInsertID = undoMan.get_netid_from_undoid(undoID); if(toInsertID.has_value()) return false; auto& layerList = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(toInsertParentID.value())->get_folder().folderList; auto insertedIt = layerList->emplace_direct(layerList, layerList->at(pos), undoMan.world, *layerData); undoMan.register_new_netid_to_existing_undoid(undoID, insertedIt->obj.get_net_id()); layerData = nullptr; return true; } bool redo(WorldUndoManager& undoMan) override { std::optional toEraseParentID = undoMan.get_netid_from_undoid(parentUndoID); if(!toEraseParentID.has_value()) return false; std::optional toEraseID = undoMan.get_netid_from_undoid(undoID); if(!toEraseID.has_value()) return false; auto& layerList = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(toEraseParentID.value())->get_folder().folderList; auto it = layerList->get(toEraseID.value()); layerData = std::make_unique(it->obj->get_undo_data(undoMan)); layerList->erase(layerList, it); return true; } void scale_up(const WorldScalar& scaleAmount) override { if(layerData) layerData->scale_up(scaleAmount); } ~DeleteLayerWorldUndoAction() {} std::unique_ptr layerData; uint32_t pos; WorldUndoManager::UndoObjectID parentUndoID; WorldUndoManager::UndoObjectID undoID; }; auto& folderList = world.netObjMan.get_obj_temporary_ref_from_id(parentID)->get_folder().folderList; auto it = folderList->get(objectID); world.undo.push(std::make_unique(std::make_unique(it->obj->get_undo_data(world.undo)), it->pos, world.undo.get_undoid_from_netid(parentID), world.undo.get_undoid_from_netid(objectID))); folderList->erase(folderList, it); } void DrawingProgramLayerManagerGUI::editing_layer_check() { auto& world = layerMan.drawP.world; class EditLayerWorldUndoAction : public WorldUndoAction { public: EditLayerWorldUndoAction(std::unique_ptr initData, WorldUndoManager::UndoObjectID initUndoID): data(std::move(initData)), undoID(initUndoID) {} std::string get_name() const override { return "Edit Layer"; } bool undo(WorldUndoManager& undoMan) override { return undo_redo(undoMan); } bool redo(WorldUndoManager& undoMan) override { return undo_redo(undoMan); } bool undo_redo(WorldUndoManager& undoMan) { std::optional toModifyID = undoMan.get_netid_from_undoid(undoID); if(!toModifyID.has_value()) return false; auto objPtr = undoMan.world.netObjMan.get_obj_temporary_ref_from_id(toModifyID.value()); auto newData = std::make_unique(objPtr->get_metainfo()); objPtr->set_metainfo(undoMan.world.drawProg.layerMan, *data); data = std::move(newData); return true; } ~EditLayerWorldUndoAction() {} std::unique_ptr data; WorldUndoManager::UndoObjectID undoID; }; if(oldSelection.size() == 1 && editingData.has_value()) { const NetworkingObjects::NetObjID& oldNetObjID = *oldSelection.begin(); auto tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(oldNetObjID); if(tempPtr) { const DrawingProgramLayerListItemMetaInfo& currentMetaData = tempPtr->get_metainfo(); if(currentMetaData != editingData.value()) world.undo.push(std::make_unique(std::make_unique(editingData.value()), world.undo.get_undoid_from_netid(oldNetObjID))); } } editingData = std::nullopt; if(selectionData.objsSelected.size() == 1) { auto tempPtr = world.netObjMan.get_obj_temporary_ref_from_id(*selectionData.objsSelected.begin()); if(tempPtr) editingData = tempPtr->get_metainfo(); } oldSelection = selectionData.objsSelected; }