From c9aba47db1111e73dc58a0a8cb96789dadeee4f0 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Thu, 25 Dec 2025 17:18:28 +0300 Subject: [PATCH] Added layer menu and layer list --- CMakeLists.txt | 4 + data/icons/layer.svg | 87 ++++++++ src/Bookmarks/BookmarkManager.cpp | 7 +- src/DrawingProgram/DrawingProgram.cpp | 4 + src/DrawingProgram/DrawingProgram.hpp | 2 + .../Layers/DrawingProgramLayer.cpp | 6 + .../Layers/DrawingProgramLayer.hpp | 2 + .../Layers/DrawingProgramLayerFolder.cpp | 7 + .../Layers/DrawingProgramLayerFolder.hpp | 4 + .../Layers/DrawingProgramLayerListItem.cpp | 11 + .../Layers/DrawingProgramLayerListItem.hpp | 1 + .../Layers/DrawingProgramLayerManager.cpp | 20 ++ .../Layers/DrawingProgramLayerManager.hpp | 17 ++ .../Layers/DrawingProgramLayerManagerGUI.cpp | 197 ++++++++++++++++++ .../Layers/DrawingProgramLayerManagerGUI.hpp | 26 +++ src/Toolbar.cpp | 36 ++++ src/Toolbar.hpp | 2 + src/UndoManager.cpp | 2 + src/World.cpp | 3 + 19 files changed, 435 insertions(+), 3 deletions(-) create mode 100644 data/icons/layer.svg create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayer.cpp create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp create mode 100644 src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c27a7a..b2a8aeb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,11 @@ set(sources "src/main.cpp" "src/DrawingProgram/EllipseDrawEditTool.cpp" "src/DrawingProgram/RectDrawEditTool.cpp" "src/DrawingProgram/TextBoxEditTool.cpp" + "src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp" + "src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp" + "src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp" + "src/DrawingProgram/Layers/DrawingProgramLayer.cpp" "src/CanvasComponents/CanvasComponentContainer.cpp" "src/CanvasComponents/CanvasComponentAllocator.cpp" "src/CanvasComponents/BrushStrokeCanvasComponent.cpp" diff --git a/data/icons/layer.svg b/data/icons/layer.svg new file mode 100644 index 0000000..e65f8a1 --- /dev/null +++ b/data/icons/layer.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Bookmarks/BookmarkManager.cpp b/src/Bookmarks/BookmarkManager.cpp index 4c83a4a..33dd3a1 100644 --- a/src/Bookmarks/BookmarkManager.cpp +++ b/src/Bookmarks/BookmarkManager.cpp @@ -68,6 +68,7 @@ void BookmarkManager::setup_list_gui(const std::string& id) { }, .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) tempPtr->get_bookmark_data().jump_to(world); CLAY_AUTO_ID({ @@ -80,14 +81,14 @@ void BookmarkManager::setup_list_gui(const std::string& id) { } if(!tempPtr->is_folder() && gui.svg_icon_button_transparent("jump button", "data/icons/jump.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { tempPtr->get_bookmark_data().jump_to(world); - return true; + 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; - return true; + isButtonClicked = true; } - return false; + return isButtonClicked; }, .moveObjectsToListAtIndex = [&](NetObjID listObj, size_t index, const std::vector& objsToInsert) { world.netObjMan.send_multi_update_messsage([&]() { diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 4c95cc3..e47739b 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -33,6 +33,7 @@ DrawingProgram::DrawingProgram(World& initWorld): world(initWorld), compCache(*this), + layerMan(*this), selection(*this) { drawTool = DrawingProgramToolBase::allocate_tool_type(*this, DrawingProgramToolType::BRUSH); @@ -43,6 +44,7 @@ void DrawingProgram::init() { components = world.netObjMan.make_obj>(); set_component_list_callbacks(); } + layerMan.init(); } void DrawingProgram::set_component_list_callbacks() { @@ -93,6 +95,7 @@ void DrawingProgram::clear_draw_cache() { void DrawingProgram::write_components_server(cereal::PortableBinaryOutputArchive& a) { components.write_create_message(a); + layerMan.write_components_server(a); } void DrawingProgram::read_components_client(cereal::PortableBinaryInputArchive& a) { @@ -102,6 +105,7 @@ void DrawingProgram::read_components_client(cereal::PortableBinaryInputArchive& }); compCache.test_rebuild(components->get_data(), true); set_component_list_callbacks(); + layerMan.read_components_client(a); } void DrawingProgram::toolbar_gui() { diff --git a/src/DrawingProgram/DrawingProgram.hpp b/src/DrawingProgram/DrawingProgram.hpp index ec743f6..bd39694 100644 --- a/src/DrawingProgram/DrawingProgram.hpp +++ b/src/DrawingProgram/DrawingProgram.hpp @@ -16,6 +16,7 @@ #include "DrawingProgramToolBase.hpp" #include #include +#include "Layers/DrawingProgramLayerManager.hpp" class World; @@ -49,6 +50,7 @@ class DrawingProgram { DrawingProgramCache compCache; + DrawingProgramLayerManager layerMan; CanvasComponentContainer::NetListOwnerPtr components; Vector4f* get_foreground_color_ptr(); diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.cpp b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp new file mode 100644 index 0000000..a9702f2 --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp @@ -0,0 +1,6 @@ +#include "DrawingProgramLayer.hpp" + +void DrawingProgramLayer::draw(SkCanvas* canvas, const DrawData& drawData) { + for(auto& p : components->get_data()) + p->obj->draw(canvas, drawData); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp index 7b98fc9..7349533 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp @@ -6,5 +6,7 @@ class DrawingProgramLayerListItem; class DrawingProgramLayer { public: + void draw(SkCanvas* canvas, const DrawData& drawData); + CanvasComponentContainer::NetListOwnerPtr components; }; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp new file mode 100644 index 0000000..54c11fc --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp @@ -0,0 +1,7 @@ +#include "DrawingProgramLayerFolder.hpp" +#include "DrawingProgramLayerListItem.hpp" + +void DrawingProgramLayerFolder::draw(SkCanvas* canvas, const DrawData& drawData) { + for(auto& p : folderList->get_data()) + p->obj->draw(canvas, drawData); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp index cd11c27..4d5f57b 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp @@ -1,10 +1,14 @@ #pragma once #include +#include +#include "../../DrawData.hpp" class DrawingProgramLayerListItem; class DrawingProgramLayerFolder { public: + void draw(SkCanvas* canvas, const DrawData& drawData); + NetworkingObjects::NetObjOwnerPtr> folderList; bool isFolderOpen = false; }; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp index 7537480..023acbe 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp @@ -43,6 +43,17 @@ DrawingProgramLayer& DrawingProgramLayerListItem::get_layer() const { return *layerData; } +void DrawingProgramLayerListItem::draw(SkCanvas* canvas, const DrawData& drawData) const { + if(displayData->visible) { + canvas->saveLayerAlphaf(nullptr, displayData->alpha); + if(folderData) + folderData->draw(canvas, drawData); + else + layerData->draw(canvas, drawData); + canvas->restore(); + } +} + void DrawingProgramLayerListItem::set_name(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, const std::string& newName) const { if(nameData && nameData->name != newName) { nameData->name = newName; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp index 9dd0396..aeaf0db 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp @@ -18,6 +18,7 @@ class DrawingProgramLayerListItem { DrawingProgramLayer& get_layer() const; static void register_class(World& w); void reassign_netobj_ids_call(); + void draw(SkCanvas* canvas, const DrawData& drawData) const; void set_name(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, const std::string& newName) const; const std::string& get_name() const; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp new file mode 100644 index 0000000..f97348b --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp @@ -0,0 +1,20 @@ +#include "DrawingProgramLayerManager.hpp" +#include "../../World.hpp" + +DrawingProgramLayerManager::DrawingProgramLayerManager(DrawingProgram& drawProg): + drawP(drawProg), // initialize drawP first for next objects to be initialized properly + listGUI(*this) +{} + +void DrawingProgramLayerManager::init() { + if(drawP.world.netObjMan.is_server()) + layerTreeRoot = drawP.world.netObjMan.make_obj_from_ptr(new DrawingProgramLayerListItem(drawP.world.netObjMan, "ROOT", true)); +} + +void DrawingProgramLayerManager::write_components_server(cereal::PortableBinaryOutputArchive& a) { + layerTreeRoot.write_create_message(a); +} + +void DrawingProgramLayerManager::read_components_client(cereal::PortableBinaryInputArchive& a) { + layerTreeRoot = drawP.world.netObjMan.read_create_message(a, nullptr); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp new file mode 100644 index 0000000..1f657ad --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "DrawingProgramLayerListItem.hpp" +#include "DrawingProgramLayerManagerGUI.hpp" + +class DrawingProgramLayerManager { + private: + DrawingProgram& drawP; + friend class DrawingProgramLayerManagerGUI; + NetworkingObjects::NetObjOwnerPtr layerTreeRoot; + + public: + DrawingProgramLayerManager(DrawingProgram& drawProg); + void init(); + void write_components_server(cereal::PortableBinaryOutputArchive& a); + void read_components_client(cereal::PortableBinaryInputArchive& a); + DrawingProgramLayerManagerGUI listGUI; +}; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp new file mode 100644 index 0000000..08a9ba3 --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp @@ -0,0 +1,197 @@ +#include "DrawingProgramLayerManagerGUI.hpp" +#include "DrawingProgramLayerManager.hpp" +#include "../DrawingProgram.hpp" +#include "../../World.hpp" +#include "../../MainProgram.hpp" + +DrawingProgramLayerManagerGUI::DrawingProgramLayerManagerGUI(DrawingProgramLayerManager& drawLayerMan): + layerMan(drawLayerMan) +{} + +void DrawingProgramLayerManagerGUI::refresh_gui_data() { + selectionData = GUIStuff::TreeListing::SelectionData(); + oldSelection.clear(); + nameToEdit.clear(); + nameForNew.clear(); + alphaValToEdit = 0.0f; +} + +void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id) { + 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) + std::cout << "Double clicked layer!" << std::endl; + 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("visible button", tempPtr->get_visible() ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", false, GUIStuff::TreeListing::ENTRY_HEIGHT)) { + tempPtr->set_visible(world.delayedUpdateObjectManager, !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) { + 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; + + for(size_t i = 0; i < objsToInsert.size(); i++) { + toEraseMap[objsToInsert[i].parent].emplace(objsToInsert[i].object); + if(newIndex != 0 && objsToInsert[i].parent == listObj && listPtr->get(objsToInsert[i].object)->pos <= index) + newIndex--; + } + + for(auto& [listToEraseFrom, setToErase] : toEraseMap) { + auto& listToEraseFromPtr = world.netObjMan.get_obj_temporary_ref_from_id(listToEraseFrom)->get_folder().folderList; + listToEraseFromPtr->erase_unordered_set(listToEraseFromPtr, setToErase, &toInsertObjPtrs); + } + + std::vector>> toInsert; + 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(newIndex + i, std::move(*it)); + toInsert.back().second.reassign_ids(); + } + listPtr->insert_ordered_list_and_send_create(listPtr, toInsert); + }, NetObjManager::SendUpdateType::SEND_TO_ALL, nullptr); + } + }, selectionData); + + + if(selectionData.objsSelected != oldSelection) { + 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(); + } + else { + nameToEdit.clear(); + alphaValToEdit = 0.0f; + } + } + else { + nameToEdit.clear(); + alphaValToEdit = 0.0f; + } + } + + if(toDeleteObject.has_value()) { + auto& folderList = world.netObjMan.get_obj_temporary_ref_from_id(toDeleteParent.value())->get_folder().folderList; + folderList->erase(folderList, 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) + create_in_proper_position(new DrawingProgramLayerListItem(world.netObjMan, nameForNew, false)); + else if(gui.svg_icon_button("create new folder", "data/icons/folderbold.svg", false, gui.SMALL_BUTTON_SIZE)) { + auto newFolderObjInfo = create_in_proper_position(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(world.delayedUpdateObjectManager, alphaValToEdit); + } + } + + gui.pop_id(); + } +} + +NetworkingObjects::NetObjOrderedListObjectInfoPtr DrawingProgramLayerManagerGUI::try_to_create_in_proper_position(DrawingProgramLayerListItem* newItem) { + using namespace NetworkingObjects; + auto& world = layerMan.drawP.world; + if(selectionData.orderedObjsSelected.empty()) + return nullptr; + auto& backObjParentObjectPair = selectionData.orderedObjsSelected.back(); + NetObjTemporaryPtr parentPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.parent); + if(!parentPtr) + return nullptr; + NetObjTemporaryPtr objectPtr = world.netObjMan.get_obj_temporary_ref_from_id(backObjParentObjectPair.object); + if(!objectPtr) + return nullptr; + if(objectPtr->is_folder()) { + objectPtr->get_folder().isFolderOpen = true; + return objectPtr->get_folder().folderList->push_back_and_send_create(objectPtr->get_folder().folderList, newItem); + } + uint32_t insertPos = parentPtr->get_folder().folderList->get(objectPtr.get_net_id())->pos; + return parentPtr->get_folder().folderList->insert_and_send_create(parentPtr->get_folder().folderList, insertPos + 1, newItem); +} + +NetworkingObjects::NetObjOrderedListObjectInfoPtr DrawingProgramLayerManagerGUI::create_in_proper_position(DrawingProgramLayerListItem* newItem) { + auto toRet = try_to_create_in_proper_position(newItem); + if(!toRet) + return layerMan.layerTreeRoot->get_folder().folderList->push_back_and_send_create(layerMan.layerTreeRoot->get_folder().folderList, newItem); + return toRet; +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp new file mode 100644 index 0000000..0d002d8 --- /dev/null +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include "../../GUIStuff/Elements/TreeListing.hpp" +#include "DrawingProgramLayerListItem.hpp" + +class World; +class DrawingProgramLayerManager; + +class DrawingProgramLayerManagerGUI { + public: + DrawingProgramLayerManagerGUI(DrawingProgramLayerManager& layerManager); + void refresh_gui_data(); + void setup_list_gui(const std::string& id); + private: + NetworkingObjects::NetObjOrderedListObjectInfoPtr try_to_create_in_proper_position(DrawingProgramLayerListItem* newItem); + NetworkingObjects::NetObjOrderedListObjectInfoPtr create_in_proper_position(DrawingProgramLayerListItem* newItem); + + GUIStuff::TreeListing::SelectionData selectionData; + std::unordered_set oldSelection; + std::string nameToEdit; + std::string nameForNew; + float alphaValToEdit = 0.0f; + + DrawingProgramLayerManager& layerMan; +}; diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 4cd386c..39e87c4 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -463,6 +463,7 @@ void Toolbar::top_toolbar() { bool menuPopUpJustOpen = false; bool bookmarkMenuPopUpJustOpen = false; bool gridMenuPopUpJustOpen = false; + bool layerMenuPopUpJustOpen = false; if(gui.svg_icon_button_transparent("Main Menu Button", "data/icons/menu.svg", menuPopUpOpen)) { menuPopUpOpen = true; menuPopUpJustOpen = true; @@ -486,6 +487,14 @@ void Toolbar::top_toolbar() { gridMenuPopUpJustOpen = true; } } + if(gui.svg_icon_button_transparent("Layer Menu Button", "data/icons/layer.svg", layerMenuPopupOpen)) { + if(layerMenuPopupOpen) + layerMenuPopupOpen = false; + else { + layerMenuPopupOpen = true; + layerMenuPopUpJustOpen = true; + } + } if(gui.svg_icon_button_transparent("Bookmark Menu Button", "data/icons/bookmark.svg", bookmarkMenuPopupOpen)) { if(bookmarkMenuPopupOpen) bookmarkMenuPopupOpen = false; @@ -502,6 +511,8 @@ void Toolbar::top_toolbar() { grid_menu(gridMenuPopUpJustOpen); if(bookmarkMenuPopupOpen) bookmark_menu(bookmarkMenuPopUpJustOpen); + if(layerMenuPopupOpen) + layer_menu(layerMenuPopUpJustOpen); if(menuPopUpOpen) { CLAY_AUTO_ID({ .layout = { @@ -810,6 +821,31 @@ void Toolbar::bookmark_menu(bool justOpened) { gui.pop_id(); } +void Toolbar::layer_menu(bool justOpened) { + gui.push_id("layer menu"); + CLAY_AUTO_ID({ + .layout = { + .sizing = {.width = CLAY_SIZING_FIT(300), .height = CLAY_SIZING_FIT(0, 600) }, + .padding = CLAY_PADDING_ALL(io->theme->padding1), + .childGap = io->theme->childGap1, + .childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_TOP}, + .layoutDirection = CLAY_TOP_TO_BOTTOM + }, + .backgroundColor = convert_vec4(io->theme->backColor1), + .cornerRadius = CLAY_CORNER_RADIUS(io->theme->windowCorners1), + .floating = {.offset = {.x = 0, .y = static_cast(io->theme->padding1)}, .attachPoints = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_BOTTOM}, .attachTo = CLAY_ATTACH_TO_PARENT} + }) { + gui.obstructing_window(); + gui.text_label_centered("Layers"); + main.world->drawProg.layerMan.listGUI.setup_list_gui("layer menu list"); + if(io->mouse.leftClick && !Clay_Hovered() && !justOpened) { + layerMenuPopupOpen = false; + main.world->drawProg.layerMan.listGUI.refresh_gui_data(); + } + } + gui.pop_id(); +} + std::unique_ptr Toolbar::build_paragraph_from_chat_message(const ChatMessage& message, float alpha) { skia::textlayout::ParagraphStyle pStyle; pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft); diff --git a/src/Toolbar.hpp b/src/Toolbar.hpp index f922652..55b9eb7 100644 --- a/src/Toolbar.hpp +++ b/src/Toolbar.hpp @@ -103,6 +103,7 @@ class Toolbar { void grid_menu(bool justOpened); void stop_displaying_grid_menu(); void bookmark_menu(bool justOpened); + void layer_menu(bool justOpened); void drawing_program_gui(); void options_menu(); void file_picker_gui(); @@ -189,6 +190,7 @@ class Toolbar { } generalSettingsOptions = GSETTINGS_GENERAL; bool bookmarkMenuPopupOpen = false; + bool layerMenuPopupOpen = false; struct GridMenu { bool popupOpen = false; diff --git a/src/UndoManager.cpp b/src/UndoManager.cpp index 4528354..9beb771 100644 --- a/src/UndoManager.cpp +++ b/src/UndoManager.cpp @@ -1,6 +1,8 @@ #include "UndoManager.hpp" #include +// NOTE: It might be possible to track net objects through their raw pointer values instead of NetObjIDs, as the NetObjID is just reassigned when moved, and it isn't reallocated. + bool UndoManager::can_undo() { return !undoQueue.empty(); } diff --git a/src/World.cpp b/src/World.cpp index ab9ebc0..28552ae 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "DrawingProgram/Layers/DrawingProgramLayerListItem.hpp" #include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp" #include "Helpers/NetworkingObjects/NetObjUnorderedSet.hpp" #include "CommandList.hpp" @@ -116,6 +117,8 @@ void World::init_net_obj_type_list() { CanvasComponentAllocator::register_class(*this); CanvasComponentContainer::register_class(netObjMan); NetworkingObjects::register_ordered_list_class(netObjMan); + DrawingProgramLayerListItem::register_class(*this); + NetworkingObjects::register_ordered_list_class(netObjMan); canvasTheme.register_class(); ClientData::register_class(*this); NetworkingObjects::register_unordered_set_class(netObjMan);