Improved undo
This commit is contained in:
parent
7e69688ca6
commit
f606fc6fc1
12 changed files with 96 additions and 99 deletions
|
|
@ -106,12 +106,12 @@ void BrushTool::tool_update() {
|
|||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->temp_update(drawP);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
// NOTE: The data isnt finallized at this point, so a BVH isn't generated for an object that you undo while youre drawing (this isnt just for brush strokes)
|
||||
// The fix for now is to generate a BVH for an object when you redo it
|
||||
drawP.add_undo_place_component(placement, controls.intermediateItem);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.addedTemporaryPoint = false;
|
||||
}
|
||||
else {
|
||||
|
|
@ -165,8 +165,6 @@ void BrushTool::gui_toolbox() {
|
|||
t.gui.text_label_centered("Brush");
|
||||
t.gui.checkbox_field("hasroundcaps", "Round Caps", &controls.hasRoundCaps);
|
||||
t.gui.slider_scalar_field("relwidth", "Size", &drawP.controls.relativeWidth, 3.0f, 40.0f);
|
||||
//t.gui.checkbox_field("drawminrel", "Drawing Min Relative To Size", &controls.drawingMinimumRelativeToSize);
|
||||
//t.gui.checkbox_field("midwayinterp", "Midway Interpolation", &controls.midwayInterpolation);
|
||||
t.gui.pop_id();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ DrawingProgram::DrawingProgram(World& initWorld):
|
|||
};
|
||||
components.clientEraseCallback = [&](const CollabListType::ObjectInfoPtr& c) {
|
||||
compCache.erase_component(c);
|
||||
selection.erase_component(c);
|
||||
delayedUpdateComponents.erase(c->obj);
|
||||
updateableComponents.erase(c->obj);
|
||||
};
|
||||
|
|
@ -140,18 +141,12 @@ void DrawingProgram::init_client_callbacks() {
|
|||
message(a);
|
||||
|
||||
std::vector<bool> didntExistPreviously = components.server_insert_ordered_vector(sortedPlacedComponents);
|
||||
if(sortedPlacedComponents.size() >= DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD) {
|
||||
parallel_loop_container(sortedPlacedComponents, [&](const auto& c){
|
||||
c->obj->final_update(*this, false);
|
||||
});
|
||||
bool isSingleThread = sortedPlacedComponents.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
|
||||
parallel_loop_container(sortedPlacedComponents, [&](const auto& c){
|
||||
c->obj->final_update(*this, isSingleThread);
|
||||
}, isSingleThread);
|
||||
if(!isSingleThread)
|
||||
force_rebuild_cache();
|
||||
}
|
||||
else {
|
||||
for(size_t i = 0; i < sortedPlacedComponents.size(); i++) {
|
||||
if(didntExistPreviously[i])
|
||||
sortedPlacedComponents[i]->obj->final_update(*this);
|
||||
}
|
||||
}
|
||||
});
|
||||
world.con.client_add_recv_callback(CLIENT_ERASE_SINGLE_COMPONENT, [&](cereal::PortableBinaryInputArchive& message) {
|
||||
ServerClientID idToErase;
|
||||
|
|
@ -443,9 +438,9 @@ void DrawingProgram::add_file_to_canvas_by_path_execute(const std::string& fileP
|
|||
img->d.imageID = imageID;
|
||||
img->final_update(*this);
|
||||
uint64_t placement = components.client_list().size();
|
||||
components.client_insert(placement, img);
|
||||
auto objAdd = components.client_insert(placement, img);
|
||||
img->client_send_place(*this);
|
||||
add_undo_place_component(placement, img);
|
||||
add_undo_place_component(objAdd);
|
||||
}
|
||||
|
||||
void DrawingProgram::add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos) {
|
||||
|
|
@ -466,9 +461,9 @@ void DrawingProgram::add_file_to_canvas_by_data(const std::string& fileName, std
|
|||
img->d.imageID = imageID;
|
||||
img->final_update(*this);
|
||||
uint64_t placement = components.client_list().size();
|
||||
components.client_insert(placement, img);
|
||||
auto objAdd = components.client_insert(placement, img);
|
||||
img->client_send_place(*this);
|
||||
add_undo_place_component(placement, img);
|
||||
add_undo_place_component(objAdd);
|
||||
}
|
||||
|
||||
void DrawingProgram::reset_tools() {
|
||||
|
|
@ -498,61 +493,26 @@ void DrawingProgram::initialize_draw_data(cereal::PortableBinaryInputArchive& a)
|
|||
compCache.test_rebuild(components.client_list(), true);
|
||||
}
|
||||
|
||||
void DrawingProgram::add_undo_place_component(uint64_t placement, const std::shared_ptr<DrawComponent>& comp) {
|
||||
void DrawingProgram::add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo) {
|
||||
world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, comp]() {
|
||||
ServerClientID compID;
|
||||
comp->client_send_erase(*this);
|
||||
components.client_erase(comp, compID);
|
||||
[&, objToUndo]() {
|
||||
objToUndo->obj->client_send_erase(*this);
|
||||
components.client_erase(objToUndo);
|
||||
reset_tools();
|
||||
return true;
|
||||
},
|
||||
[&, comp, placement]() {
|
||||
if(comp->collabListInfo.lock())
|
||||
[&, objToUndo]() {
|
||||
if(objToUndo->obj->collabListInfo.lock())
|
||||
return false;
|
||||
components.client_insert(placement, comp);
|
||||
comp->final_update(*this); // Method to recover after an undo that happened while drawing a component
|
||||
comp->client_send_place(*this);
|
||||
components.client_insert(objToUndo);
|
||||
objToUndo->obj->final_update(*this); // Method to recover after an undo that happened while drawing a component
|
||||
objToUndo->obj->client_send_place(*this);
|
||||
reset_tools();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DrawingProgram::add_undo_place_components(uint64_t placement, const std::vector<std::shared_ptr<DrawComponent>>& comps) {
|
||||
world.undo.push(UndoManager::UndoRedoPair{
|
||||
[&, comps]() {
|
||||
bool toRet = true;
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
for(auto& c : comps) {
|
||||
if(!c->collabListInfo.lock())
|
||||
toRet = false;
|
||||
else {
|
||||
idsToErase.emplace(c->collabListInfo.lock()->id);
|
||||
ServerClientID compID;
|
||||
components.client_erase(c, compID);
|
||||
}
|
||||
}
|
||||
DrawComponent::client_send_erase_set(*this, idsToErase);
|
||||
reset_tools();
|
||||
return toRet;
|
||||
},
|
||||
[&, comps, placement]() {
|
||||
for(auto& c : comps)
|
||||
if(c->collabListInfo.lock())
|
||||
return false;
|
||||
for(auto& c : comps | std::views::reverse) {
|
||||
components.client_insert(placement, c);
|
||||
c->final_update(*this); // Method to recover after an undo that happened while drawing a component
|
||||
c->client_send_place(*this);
|
||||
}
|
||||
reset_tools();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ClientPortionID DrawingProgram::get_max_id(ServerPortionID serverID) {
|
||||
ClientPortionID maxClientID = 0;
|
||||
auto& cList = components.client_list();
|
||||
|
|
|
|||
|
|
@ -76,8 +76,7 @@ class DrawingProgram {
|
|||
|
||||
void draw_drag_circle(SkCanvas* canvas, const Vector2f& pos, const SkColor4f& c, const DrawData& drawData, float radiusMultiplier = 1.0f);
|
||||
|
||||
void add_undo_place_component(uint64_t placement, const std::shared_ptr<DrawComponent>& comp);
|
||||
void add_undo_place_components(uint64_t placement, const std::vector<std::shared_ptr<DrawComponent>>& comps);
|
||||
void add_undo_place_component(const CollabListType::ObjectInfoPtr& objToUndo);
|
||||
|
||||
BrushTool brushTool;
|
||||
EraserTool eraserTool;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@ const std::unordered_set<CollabListType::ObjectInfoPtr>& DrawingProgramSelection
|
|||
return selectedSet;
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::erase_component(const CollabListType::ObjectInfoPtr& objToCheck) {
|
||||
if(is_selected(objToCheck)) {
|
||||
cache.erase_component(objToCheck);
|
||||
selectedSet.erase(objToCheck);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCollision::ColliderCollection<float>& cC) {
|
||||
auto cCWorld = drawP.world.drawData.cam.c.collider_to_world<SCollision::ColliderCollection<WorldScalar>, SCollision::ColliderCollection<float>>(cC);
|
||||
|
||||
|
|
@ -32,7 +39,7 @@ void DrawingProgramSelection::add_from_cam_coord_collider_to_selection(const SCo
|
|||
}
|
||||
};
|
||||
drawP.compCache.traverse_bvh_erase_function(cCWorld.bounds, [&](const auto& bvhNode, auto& comps) {
|
||||
if(bvhNode &&
|
||||
if(bvhNode && (bvhNode->coords.inverseScale << 9) < drawP.world.drawData.cam.c.inverseScale &&
|
||||
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.min)) &&
|
||||
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.max)) &&
|
||||
SCollision::collide(cC, drawP.world.drawData.cam.c.to_space(bvhNode->bounds.top_right())) &&
|
||||
|
|
@ -78,10 +85,24 @@ void DrawingProgramSelection::deselect_all() {
|
|||
for(auto& transformedObj : a)
|
||||
transformsFrom.emplace_back(transformedObj, transformedObj->obj->coords);
|
||||
|
||||
selectedSet.clear();
|
||||
cache.clear();
|
||||
transformOpHappening = TransformOperation::NONE;
|
||||
|
||||
bool isSingleThread = a.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
|
||||
parallel_loop_container(a, [&](auto& obj) {
|
||||
obj->obj->coords = selectionTransformCoords.other_coord_space_from_this_space(obj->obj->coords);
|
||||
obj->obj->final_update(drawP, false);
|
||||
});
|
||||
obj->obj->finalize_update(drawP, false);
|
||||
}, isSingleThread);
|
||||
if(!isSingleThread)
|
||||
drawP.force_rebuild_cache();
|
||||
else {
|
||||
for(auto& obj : a)
|
||||
drawP.compCache.add_component(obj);
|
||||
}
|
||||
|
||||
selectionTransformCoords = CoordSpaceHelper();
|
||||
|
||||
std::vector<std::pair<CollabListType::ObjectInfoPtr, CoordSpaceHelper>> transformsTo;
|
||||
std::vector<std::pair<ServerClientID, CoordSpaceHelper>> transformsToSend;
|
||||
for(auto& transformedObj : a) {
|
||||
|
|
@ -101,17 +122,19 @@ void DrawingProgramSelection::deselect_all() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool isSingleThread = a.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
|
||||
parallel_loop_container(transformsFrom, [&](auto& p) {
|
||||
auto& [comp, coords] = p;
|
||||
comp->obj->coords = coords;
|
||||
comp->obj->finalize_update(drawP, false);
|
||||
});
|
||||
comp->obj->finalize_update(drawP, isSingleThread);
|
||||
}, isSingleThread);
|
||||
|
||||
DrawComponent::client_send_transform_many(drawP, transformsToSend);
|
||||
|
||||
drawP.reset_tools();
|
||||
|
||||
drawP.force_rebuild_cache();
|
||||
if(!isSingleThread)
|
||||
drawP.force_rebuild_cache();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
|
@ -125,28 +148,23 @@ void DrawingProgramSelection::deselect_all() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool isSingleThread = a.size() < DrawingProgramCache::MINIMUM_COMPONENTS_TO_START_REBUILD;
|
||||
parallel_loop_container(transformsTo, [&](auto& p) {
|
||||
auto& [comp, coords] = p;
|
||||
comp->obj->coords = coords;
|
||||
comp->obj->finalize_update(drawP, false);
|
||||
});
|
||||
comp->obj->finalize_update(drawP, isSingleThread);
|
||||
}, isSingleThread);
|
||||
|
||||
DrawComponent::client_send_transform_many(drawP, transformsToSend);
|
||||
|
||||
drawP.reset_tools();
|
||||
|
||||
drawP.force_rebuild_cache();
|
||||
if(!isSingleThread)
|
||||
drawP.force_rebuild_cache();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
selectedSet.clear();
|
||||
cache.clear();
|
||||
selectionTransformCoords = CoordSpaceHelper();
|
||||
drawP.force_rebuild_cache();
|
||||
transformOpHappening = TransformOperation::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ class DrawingProgramSelection {
|
|||
void draw_gui(SkCanvas* canvas, const DrawData& drawData);
|
||||
bool is_something_selected();
|
||||
bool mouse_collided_with_selection();
|
||||
void erase_component(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
bool is_selected(const CollabListType::ObjectInfoPtr& objToCheck);
|
||||
const std::unordered_set<CollabListType::ObjectInfoPtr>& get_selected_set();
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -109,9 +109,9 @@ void EllipseDrawTool::tool_update() {
|
|||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
drawP.add_undo_place_component(placement, controls.intermediateItem);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.intermediateItem->temp_update(drawP);
|
||||
controls.drawStage = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ void EraserTool::reset_tool() {
|
|||
if(!erasedComponents.empty()) {
|
||||
DrawingProgramCache::move_components_from_bvh_nodes_to_set(erasedComponents, erasedBVHNodes);
|
||||
|
||||
std::unordered_set<ServerClientID> idsToErase;
|
||||
std::unordered_set<ServerClientID> idsToErase1;
|
||||
for(auto& c : erasedComponents)
|
||||
idsToErase.emplace(c->id);
|
||||
DrawComponent::client_send_erase_set(drawP, idsToErase);
|
||||
idsToErase1.emplace(c->id);
|
||||
DrawComponent::client_send_erase_set(drawP, idsToErase1);
|
||||
drawP.components.client_erase_set(erasedComponents);
|
||||
|
||||
drawP.world.undo.push(UndoManager::UndoRedoPair{
|
||||
|
|
|
|||
|
|
@ -109,10 +109,10 @@ void RectDrawTool::tool_update() {
|
|||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
controls.intermediateID = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->temp_update(drawP);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
drawP.add_undo_place_component(placement, controls.intermediateItem);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.drawStage = 1;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ class RectDrawTool {
|
|||
int fillStrokeMode = 1;
|
||||
int drawStage = 0;
|
||||
std::shared_ptr<DrawRectangle> intermediateItem;
|
||||
ServerClientID intermediateID;
|
||||
} controls;
|
||||
|
||||
DrawingProgram& drawP;
|
||||
|
|
|
|||
|
|
@ -227,9 +227,9 @@ void TextBoxTool::tool_update() {
|
|||
controls.intermediateItem->coords = drawP.world.drawData.cam.c;
|
||||
controls.intermediateItem->lastUpdateTime = std::chrono::steady_clock::now();
|
||||
uint64_t placement = drawP.components.client_list().size();
|
||||
drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
auto objAdd = drawP.components.client_insert(placement, controls.intermediateItem);
|
||||
controls.intermediateItem->client_send_place(drawP);
|
||||
drawP.add_undo_place_component(placement, controls.intermediateItem);
|
||||
drawP.add_undo_place_component(objAdd);
|
||||
controls.drawStage = 1;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue