Layers work with brush tool

This commit is contained in:
Yousef Khadadeh 2025-12-26 10:10:19 +03:00
parent 067abc8553
commit 702df53794
13 changed files with 151 additions and 57 deletions

View file

@ -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<BrushStrokeCanvasComponent&>(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<BrushStrokeCanvasComponent&>(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<CanvasComponentContainer>& containerPtr = objInfoBeingEdited->obj;

View file

@ -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<ImageCanvasComponent&>(droppedDownFile.comp->obj->get_comp()).draw_download_progress_bar(canvas, drawData, droppedDownFile.downData->progress);
// if(!drawData.main->takingScreenshot) {
// for(auto& droppedDownFile : droppedDownloadingFiles)
// static_cast<ImageCanvasComponent&>(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<ImageCanvasComponent&>(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<ImageCanvasComponent&>(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);
}

View file

@ -1,6 +1,22 @@
#include "DrawingProgramLayer.hpp"
#include "DrawingProgramLayerManager.hpp"
#include "../DrawingProgram.hpp"
#include <Helpers/Parallel.hpp>
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);
});
}

View file

@ -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;
};

View file

@ -1,7 +1,22 @@
#include "DrawingProgramLayerFolder.hpp"
#include "DrawingProgramLayerListItem.hpp"
#include "DrawingProgramLayer.hpp"
#include <Helpers/Parallel.hpp>
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);
}

View file

@ -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<NetworkingObjects::NetObjOrderedList<DrawingProgramLayerListItem>> folderList;
bool isFolderOpen = false;

View file

@ -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;
}

View file

@ -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;

View file

@ -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<DrawingProgramLayerListItem>(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<DrawingProgramLayerListItem>(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);
}

View file

@ -6,12 +6,24 @@ class DrawingProgramLayerManager {
private:
DrawingProgram& drawP;
friend class DrawingProgramLayerManagerGUI;
friend class DrawingProgramLayerListItem;
friend class DrawingProgramLayer;
friend class DrawingProgramLayerFolder;
NetworkingObjects::NetObjOwnerPtr<DrawingProgramLayerListItem> layerTreeRoot;
NetworkingObjects::NetObjWeakPtr<DrawingProgramLayerListItem> 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 <typename T> 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;
};

View file

@ -66,7 +66,16 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool&
auto tempPtr = world.netObjMan.get_obj_temporary_ref_from_id<DrawingProgramLayerListItem>(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<DrawingProgramLayerListItem> 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<DrawingProgramLayerListItem> 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;
}