From 702df537946950b076b2608b70161c2643121142 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Fri, 26 Dec 2025 10:10:19 +0300 Subject: [PATCH] Layers work with brush tool --- .../NetworkingObjects/NetObjWeakPtr.hpp | 2 +- .../CanvasComponentContainer.cpp | 16 +++--- src/DrawingProgram/BrushTool.cpp | 28 +++++----- src/DrawingProgram/DrawingProgram.cpp | 53 ++++++++++--------- .../Layers/DrawingProgramLayer.cpp | 16 ++++++ .../Layers/DrawingProgramLayer.hpp | 3 ++ .../Layers/DrawingProgramLayerFolder.cpp | 17 +++++- .../Layers/DrawingProgramLayerFolder.hpp | 3 ++ .../Layers/DrawingProgramLayerListItem.cpp | 14 +++++ .../Layers/DrawingProgramLayerListItem.hpp | 3 ++ .../Layers/DrawingProgramLayerManager.cpp | 16 +++++- .../Layers/DrawingProgramLayerManager.hpp | 14 ++++- .../Layers/DrawingProgramLayerManagerGUI.cpp | 23 +++++--- 13 files changed, 151 insertions(+), 57 deletions(-) diff --git a/include/Helpers/NetworkingObjects/NetObjWeakPtr.hpp b/include/Helpers/NetworkingObjects/NetObjWeakPtr.hpp index 8c1868f..b6b1090 100644 --- a/include/Helpers/NetworkingObjects/NetObjWeakPtr.hpp +++ b/include/Helpers/NetworkingObjects/NetObjWeakPtr.hpp @@ -43,7 +43,7 @@ namespace NetworkingObjects { return NetObjWeakPtr(objMan, id); } bool expired() const { - return !objMan || objMan->objectData.contains(id); + return (!objMan) || (!objMan->objectData.contains(id)); } NetObjTemporaryPtr lock() const { if(!objMan) diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index cf97378..ffbe513 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -65,20 +65,20 @@ void CanvasComponentContainer::set_owner_obj_info(const ObjInfoSharedPtr& ownerO } void CanvasComponentContainer::commit_update(DrawingProgram& drawP) { - auto lockedObjInfo = objInfo.lock(); - if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt - drawP.preupdate_component(lockedObjInfo); + //auto lockedObjInfo = objInfo.lock(); + //if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt + // drawP.preupdate_component(lockedObjInfo); get_comp().initialize_draw_data(drawP); calculate_world_bounds(); - drawP.preupdate_component(lockedObjInfo); + //drawP.preupdate_component(lockedObjInfo); } void CanvasComponentContainer::commit_transform(DrawingProgram& drawP) { - auto lockedObjInfo = objInfo.lock(); - if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt - drawP.preupdate_component(lockedObjInfo); + //auto lockedObjInfo = objInfo.lock(); + //if(worldAABB.has_value()) // This is the only point where worldAABB could be nullopt + // drawP.preupdate_component(lockedObjInfo); calculate_world_bounds(); - drawP.preupdate_component(lockedObjInfo); + //drawP.preupdate_component(lockedObjInfo); } void CanvasComponentContainer::commit_update_dont_invalidate_cache(DrawingProgram& drawP) { diff --git a/src/DrawingProgram/BrushTool.cpp b/src/DrawingProgram/BrushTool.cpp index 6d3f3b8..e79c113 100644 --- a/src/DrawingProgram/BrushTool.cpp +++ b/src/DrawingProgram/BrushTool.cpp @@ -90,20 +90,22 @@ void BrushTool::tool_update() { if(isPenDown) { if(!isDrawing) { - isDrawing = true; - CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::BRUSHSTROKE); - BrushStrokeCanvasComponent& newBrushStroke = static_cast(newBrushStrokeContainer->get_comp()); + if(drawP.layerMan.is_a_layer_being_edited()) { + isDrawing = true; + CanvasComponentContainer* newBrushStrokeContainer = new CanvasComponentContainer(drawP.world.netObjMan, CanvasComponentType::BRUSHSTROKE); + BrushStrokeCanvasComponent& newBrushStroke = static_cast(newBrushStrokeContainer->get_comp()); - BrushStrokeCanvasComponentPoint p; - p.pos = drawP.world.main.input.mouse.pos; - p.width = drawP.controls.relativeWidth * penWidth; - prevPointUnaltered = p.pos; - newBrushStroke.d->points.emplace_back(p); - newBrushStroke.d->color = drawP.controls.foregroundColor; - newBrushStroke.d->hasRoundCaps = hasRoundCaps; - newBrushStrokeContainer->coords = drawP.world.drawData.cam.c; - objInfoBeingEdited = drawP.components->push_back_and_send_create(drawP.components, newBrushStrokeContainer); - addedTemporaryPoint = false; + BrushStrokeCanvasComponentPoint p; + p.pos = drawP.world.main.input.mouse.pos; + p.width = drawP.controls.relativeWidth * penWidth; + prevPointUnaltered = p.pos; + newBrushStroke.d->points.emplace_back(p); + newBrushStroke.d->color = drawP.controls.foregroundColor; + newBrushStroke.d->hasRoundCaps = hasRoundCaps; + newBrushStrokeContainer->coords = drawP.world.drawData.cam.c; + objInfoBeingEdited = drawP.layerMan.add_component_to_layer_being_edited(newBrushStrokeContainer); + addedTemporaryPoint = false; + } } else { NetworkingObjects::NetObjOwnerPtr& containerPtr = objInfoBeingEdited->obj; diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index e47739b..6a3feca 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -566,35 +566,36 @@ void DrawingProgram::save_file(cereal::PortableBinaryOutputArchive& a) const { } void DrawingProgram::draw(SkCanvas* canvas, const DrawData& drawData) { - if(drawData.dontUseDrawProgCache) { - for(auto& c : components->get_data()) - c->obj->draw(canvas, drawData); - } - else { - canvas->saveLayer(nullptr, nullptr); - canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); - compCache.refresh_all_draw_cache(drawData); - compCache.draw_components_to_canvas(canvas, drawData, {}); - canvas->saveLayer(nullptr, nullptr); - selection.draw_components(canvas, drawData); - canvas->restore(); - canvas->restore(); + layerMan.draw(canvas, drawData); + //if(drawData.dontUseDrawProgCache) { + // for(auto& c : components->get_data()) + // c->obj->draw(canvas, drawData); + //} + //else { + // canvas->saveLayer(nullptr, nullptr); + // canvas->clear(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f}); + // compCache.refresh_all_draw_cache(drawData); + // compCache.draw_components_to_canvas(canvas, drawData, {}); + // canvas->saveLayer(nullptr, nullptr); + // selection.draw_components(canvas, drawData); + // canvas->restore(); + // canvas->restore(); - if(!drawData.main->takingScreenshot) { - for(auto& droppedDownFile : droppedDownloadingFiles) - static_cast(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress); + // if(!drawData.main->takingScreenshot) { + // for(auto& droppedDownFile : droppedDownloadingFiles) + // static_cast(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress); - for(auto& c : updateableComponents) { - if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE) { - auto& img = static_cast(c->obj->get_comp()); - float progress = drawData.main->world->rMan.get_resource_retrieval_progress(img.d.imageID); - img.draw_download_progress_bar(canvas, drawData, progress); - } - } - } + // for(auto& c : updateableComponents) { + // if(c->obj->get_comp().get_type() == CanvasComponentType::IMAGE) { + // auto& img = static_cast(c->obj->get_comp()); + // float progress = drawData.main->world->rMan.get_resource_retrieval_progress(img.d.imageID); + // img.draw_download_progress_bar(canvas, drawData, progress); + // } + // } + // } - selection.draw_gui(canvas, drawData); - } + // selection.draw_gui(canvas, drawData); + //} drawTool->draw(canvas, drawData); } diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.cpp b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp index a9702f2..716523d 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayer.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.cpp @@ -1,6 +1,22 @@ #include "DrawingProgramLayer.hpp" +#include "DrawingProgramLayerManager.hpp" +#include "../DrawingProgram.hpp" +#include void DrawingProgramLayer::draw(SkCanvas* canvas, const DrawData& drawData) { for(auto& p : components->get_data()) p->obj->draw(canvas, drawData); } + +void DrawingProgramLayer::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) { + components->set_insert_callback([&](const CanvasComponentContainer::ObjInfoSharedPtr& c) { + c->obj->set_owner_obj_info(c); + c->obj->commit_update(layerMan.drawP); // Run commit update on insert so that world bounds are calculated + }); +} + +void DrawingProgramLayer::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) { + parallel_loop_container(components->get_data(), [&layerMan](auto& comp) { + comp->obj->commit_update_dont_invalidate_cache(layerMan.drawP); + }); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp index 7349533..4694d1b 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayer.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayer.hpp @@ -3,10 +3,13 @@ #include "../../CanvasComponents/CanvasComponent.hpp" class DrawingProgramLayerListItem; +class DrawingProgramLayerManager; class DrawingProgramLayer { public: void draw(SkCanvas* canvas, const DrawData& drawData); + void set_component_list_callbacks(DrawingProgramLayerManager& layerMan); + void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); CanvasComponentContainer::NetListOwnerPtr components; }; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp index 54c11fc..08f4827 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp @@ -1,7 +1,22 @@ #include "DrawingProgramLayerFolder.hpp" #include "DrawingProgramLayerListItem.hpp" +#include "DrawingProgramLayer.hpp" +#include void DrawingProgramLayerFolder::draw(SkCanvas* canvas, const DrawData& drawData) { - for(auto& p : folderList->get_data()) + for(auto& p : folderList->get_data() | std::views::reverse) p->obj->draw(canvas, drawData); } + +void DrawingProgramLayerFolder::set_component_list_callbacks(DrawingProgramLayerManager& layerMan) { + folderList->set_insert_callback([&](auto& c) { + c->obj->set_component_list_callbacks(layerMan); + }); + for(auto& listItem : folderList->get_data()) + listItem->obj->set_component_list_callbacks(layerMan); +} + +void DrawingProgramLayerFolder::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) { + for(auto& listItem : folderList->get_data()) + listItem->obj->commit_update_dont_invalidate_cache(layerMan); +} diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp index 4d5f57b..d008455 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerFolder.hpp @@ -4,10 +4,13 @@ #include "../../DrawData.hpp" class DrawingProgramLayerListItem; +class DrawingProgramLayerManager; class DrawingProgramLayerFolder { public: void draw(SkCanvas* canvas, const DrawData& drawData); + void set_component_list_callbacks(DrawingProgramLayerManager& layerMan); + void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); NetworkingObjects::NetObjOwnerPtr> folderList; bool isFolderOpen = false; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp index e0222d6..92a3f11 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp @@ -32,6 +32,20 @@ void DrawingProgramLayerListItem::reassign_netobj_ids_call() { displayData.reassign_ids(); } +void DrawingProgramLayerListItem::set_component_list_callbacks(DrawingProgramLayerManager &layerMan) { + if(folderData) + folderData->set_component_list_callbacks(layerMan); + else + layerData->set_component_list_callbacks(layerMan); +} + +void DrawingProgramLayerListItem::commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan) { + if(folderData) + folderData->commit_update_dont_invalidate_cache(layerMan); + else + layerData->commit_update_dont_invalidate_cache(layerMan); +} + bool DrawingProgramLayerListItem::is_folder() const { return folderData != nullptr; } diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp index 9282711..d5f7072 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp @@ -9,6 +9,7 @@ class DrawingProgramLayerFolder; class DrawingProgramLayer; class World; +class DrawingProgramLayerManager; class DrawingProgramLayerListItem { public: @@ -19,6 +20,8 @@ class DrawingProgramLayerListItem { DrawingProgramLayer& get_layer() const; static void register_class(World& w); void reassign_netobj_ids_call(); + void set_component_list_callbacks(DrawingProgramLayerManager &layerMan); + void commit_update_dont_invalidate_cache(DrawingProgramLayerManager& layerMan); void draw(SkCanvas* canvas, const DrawData& drawData) const; void set_name(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, const std::string& newName) const; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp index f97348b..b36e8f2 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.cpp @@ -7,8 +7,11 @@ DrawingProgramLayerManager::DrawingProgramLayerManager(DrawingProgram& drawProg) {} void DrawingProgramLayerManager::init() { - if(drawP.world.netObjMan.is_server()) + if(drawP.world.netObjMan.is_server()) { layerTreeRoot = drawP.world.netObjMan.make_obj_from_ptr(new DrawingProgramLayerListItem(drawP.world.netObjMan, "ROOT", true)); + layerTreeRoot->get_folder().set_component_list_callbacks(*this); // Only need to call set_component_list_callbacks on root, and the rest will get the callbacks set as well + editingLayer = layerTreeRoot->get_folder().folderList->emplace_back_direct(layerTreeRoot->get_folder().folderList, drawP.world.netObjMan, "First Layer", false)->obj; + } } void DrawingProgramLayerManager::write_components_server(cereal::PortableBinaryOutputArchive& a) { @@ -17,4 +20,15 @@ void DrawingProgramLayerManager::write_components_server(cereal::PortableBinaryO void DrawingProgramLayerManager::read_components_client(cereal::PortableBinaryInputArchive& a) { layerTreeRoot = drawP.world.netObjMan.read_create_message(a, nullptr); + layerTreeRoot->commit_update_dont_invalidate_cache(*this); + layerTreeRoot->set_component_list_callbacks(*this); +} + +bool DrawingProgramLayerManager::is_a_layer_being_edited() { + return !editingLayer.expired(); +} + +void DrawingProgramLayerManager::draw(SkCanvas* canvas, const DrawData& drawData) { + if(layerTreeRoot) + layerTreeRoot->draw(canvas, drawData); } diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp index 1f657ad..5fdcb4b 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManager.hpp @@ -6,12 +6,24 @@ class DrawingProgramLayerManager { private: DrawingProgram& drawP; friend class DrawingProgramLayerManagerGUI; + friend class DrawingProgramLayerListItem; + friend class DrawingProgramLayer; + friend class DrawingProgramLayerFolder; NetworkingObjects::NetObjOwnerPtr layerTreeRoot; - + NetworkingObjects::NetObjWeakPtr editingLayer; public: + DrawingProgramLayerManager(DrawingProgram& drawProg); void init(); + void draw(SkCanvas* canvas, const DrawData& drawData); void write_components_server(cereal::PortableBinaryOutputArchive& a); void read_components_client(cereal::PortableBinaryInputArchive& a); + bool is_a_layer_being_edited(); + template CanvasComponentContainer::ObjInfoSharedPtr add_component_to_layer_being_edited(T* newObj) { + auto editLayerPtr = editingLayer.lock(); + if(!editLayerPtr) + return nullptr; + return editLayerPtr->get_layer().components->push_back_and_send_create(editLayerPtr->get_layer().components, newObj); + } DrawingProgramLayerManagerGUI listGUI; }; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp index 6249982..d816ae2 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp @@ -66,7 +66,16 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool& 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; + 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)}, @@ -152,8 +161,10 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool& 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)); + if(gui.svg_icon_button("create new layer", "data/icons/plusbold.svg", false, gui.SMALL_BUTTON_SIZE) || addByEnter) { + auto newLayerObjInfo = create_in_proper_position(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_in_proper_position(new DrawingProgramLayerListItem(world.netObjMan, nameForNew, true)); newFolderObjInfo->obj->get_folder().isFolderOpen = true; @@ -197,15 +208,15 @@ NetworkingObjects::NetObjOrderedListObjectInfoPtr D 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); + return objectPtr->get_folder().folderList->insert_and_send_create(objectPtr->get_folder().folderList, 0, 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); + return parentPtr->get_folder().folderList->insert_and_send_create(parentPtr->get_folder().folderList, insertPos, 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 layerMan.layerTreeRoot->get_folder().folderList->insert_and_send_create(layerMan.layerTreeRoot->get_folder().folderList, 0, newItem); return toRet; }