diff --git a/src/helper/geom-pathvector_nodesatellites.cpp b/src/helper/geom-pathvector_nodesatellites.cpp index ce6b18ade634c746cda5de40b8ec7e31d80e071e..f822a2be884894b6a86fbbb9b47180372a284ce4 100644 --- a/src/helper/geom-pathvector_nodesatellites.cpp +++ b/src/helper/geom-pathvector_nodesatellites.cpp @@ -197,51 +197,36 @@ void PathVectorNodeSatellites::updateNodeSatelliteType(NodeSatelliteType nodesat } } -void PathVectorNodeSatellites::recalculateForNewPathVector(Geom::PathVector const pathv, NodeSatellite const S) +std::optional PathVectorNodeSatellites::findNearSatelite(Geom::Point point, double precission) { - // pathv && _pathvector came here: - // * with different number of nodes - // * without empty subpats - // * _pathvector and nodesatellites (old data) are paired - NodeSatellites nodesatellites; + size_t npaths = _pathvector.size(); + for (size_t i = 0; i < npaths; ++i) { + size_t count = count_path_nodes(_pathvector[i]); + for (size_t j = 0; j < count; ++j) { + if (i < _nodesatellites.size() && j < _nodesatellites[i].size()) { + if (Geom::are_near(point,_pathvector[i][j].initialPoint(), precission)) { + return std::make_optional(_nodesatellites[i][j]); + } + } + } + } + return std::nullopt; +} - // TODO evaluate fix on nodes at same position - // size_t number_nodes = count_pathvector_nodes(pathv); - // size_t previous_number_nodes = getTotalNodeSatellites(); +void PathVectorNodeSatellites::adjustForNewPath(Geom::PathVector const pathv, double precission) +{ + NodeSatellites nodesatellites; + // TODO issue for nodes in same place size_t npaths = pathv.size(); + NodeSatellite ns = _nodesatellite; + if (_nodesatellites.size() && _nodesatellites[0].size()) { + ns.setNodeSatellitesType(_nodesatellites[0][0].getNodeSatellitesTypeGchar()); + } for (size_t i = 0; i < npaths; ++i) { std::vector path_nodesatellites; size_t count = count_path_nodes(pathv[i]); for (size_t j = 0; j < count; ++j) { - bool found = false; - for (size_t k = 0; k < _pathvector.size(); ++k) { - size_t countnodes = count_path_nodes(_pathvector[k]); - size_t countcurves = count_path_curves(_pathvector[k]); - if (!_nodesatellites.empty() && !_nodesatellites[k].empty()) { - assert(countnodes == _nodesatellites[k].size()); - for (size_t l = 0; l < countcurves; ++l) { - if (Geom::are_near(_pathvector[k][l].initialPoint(), pathv[i][j].initialPoint(), 0.001)) { // epsilon is not enought big - path_nodesatellites.push_back(_nodesatellites[k][l]); - found = true; - break; - } - } - } - if (found) { - break; - } - } - if (!found) { - if (_pathvector.empty()) { - if (i < _nodesatellites.size() && j < _nodesatellites[i].size()) { - path_nodesatellites.push_back(_nodesatellites[i][j]); - } else { - path_nodesatellites.push_back(S); - } - } else { - path_nodesatellites.push_back(S); - } - } + path_nodesatellites.push_back(findNearSatelite(pathv[i][j].initialPoint(), precission).value_or(ns)); } nodesatellites.push_back(path_nodesatellites); } diff --git a/src/helper/geom-pathvector_nodesatellites.h b/src/helper/geom-pathvector_nodesatellites.h index 4631b320dc1b5e9e5b0030551e534671eee3623f..64b1990e0dedbeb2a74b2cac296ac53179db0fd0 100644 --- a/src/helper/geom-pathvector_nodesatellites.h +++ b/src/helper/geom-pathvector_nodesatellites.h @@ -34,6 +34,7 @@ public: void setNodeSatellites(NodeSatellites nodesatellites); size_t getTotalNodeSatellites(); void setSelected(std::vector selected); + void setDefaultNodeSatellite(NodeSatellite nodesatellite) { _nodesatellite = nodesatellite; }; void updateSteps(size_t steps, bool apply_no_radius, bool apply_with_radius, bool only_selected); void updateAmount(double radius, bool apply_no_radius, bool apply_with_radius, bool only_selected, bool use_knot_distance, bool flexible); @@ -41,11 +42,13 @@ public: void updateNodeSatelliteType(NodeSatelliteType nodesatellitetype, bool apply_no_radius, bool apply_with_radius, bool only_selected); std::pair getIndexData(size_t index); - void recalculateForNewPathVector(Geom::PathVector const pathv, NodeSatellite const S); + std::optional findNearSatelite(Geom::Point point, double precission = 0.01); + void adjustForNewPath(Geom::PathVector const pathv, double precission = 0.01); private: Geom::PathVector _pathvector; NodeSatellites _nodesatellites; + NodeSatellite _nodesatellite; }; #endif //SEEN_PATHVECTORSATELLITES_H diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index ad4f8f9e91c1572954e1d9a36a389e61ef14dbf5..66f923fab5eda3ac8c528fff2d90966442912c8f 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -47,12 +47,11 @@ #include "live_effects/lpe-interpolate_points.h" #include "live_effects/lpe-jointype.h" #include "live_effects/lpe-knot.h" -#include "live_effects/lpe-lattice2.h" #include "live_effects/lpe-lattice.h" +#include "live_effects/lpe-lattice2.h" #include "live_effects/lpe-line_segment.h" #include "live_effects/lpe-measure-segments.h" #include "live_effects/lpe-mirror_symmetry.h" -#include "live_effects/lpeobject.h" #include "live_effects/lpe-offset.h" #include "live_effects/lpe-parallel.h" #include "live_effects/lpe-path_length.h" @@ -64,8 +63,8 @@ #include "live_effects/lpe-powerstroke.h" #include "live_effects/lpe-pts2ellipse.h" #include "live_effects/lpe-recursiveskeleton.h" -#include "live_effects/lpe-roughen.h" #include "live_effects/lpe-rough-hatches.h" +#include "live_effects/lpe-roughen.h" #include "live_effects/lpe-ruler.h" #include "live_effects/lpe-show_handles.h" #include "live_effects/lpe-simplify.h" @@ -79,6 +78,7 @@ #include "live_effects/lpe-tiling.h" #include "live_effects/lpe-transform_2pts.h" #include "live_effects/lpe-vonkoch.h" +#include "live_effects/lpeobject.h" #include "message-stack.h" #include "object/sp-defs.h" #include "object/sp-root.h" @@ -86,9 +86,11 @@ #include "path-chemistry.h" #include "ui/icon-loader.h" #include "ui/pack.h" +#include "ui/shape-editor.h" #include "ui/tools/node-tool.h" #include "ui/tools/pen-tool.h" #include "xml/sp-css-attr.h" +#include "helper/geom.h" namespace Inkscape { namespace LivePathEffect { @@ -1435,7 +1437,6 @@ Effect::update_satellites() { } } - /** * Is performed each time before the effect is updated. */ @@ -1465,19 +1466,52 @@ void Effect::doOnException(SPLPEItem const * /*lpeitem*/) pathvector_after_effect = pathvector_before_effect; } - void Effect::doOnRemove (SPLPEItem const* /*lpeitem*/) { } + void Effect::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) { } + +void Effect::adjustForNewPath(bool removed) +{ + _adjust_path = true; +} + //secret impl methods (shhhh!) void Effect::doAfterEffect_impl(SPLPEItem const *lpeitem, SPCurve *curve) { doAfterEffect(lpeitem, curve); is_load = false; is_applied = false; + _adjust_path = false; +} + + +void Effect::reloadKnots(SPLPEItem const *lpeitem) +{ + if (!is_current) { + return; + } + if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { + if (auto nt = dynamic_cast(desktop->getTool())) { + auto lpeitem_mutable = const_cast(lpeitem); + SPItem *item = cast(lpeitem_mutable); + auto lpeitems = getCurrrentLPEItems(); + if (!lpeitems.empty()) { + item = cast(lpeitems.front()); + } + for (auto &_shape_editor : nt->_shape_editors) { + if (item && item == _shape_editor.first) { + auto shape_editor = _shape_editor.second.get(); + shape_editor->unset_item(); + shape_editor->set_item(item); + return; + } + } + } + } } void Effect::doOnRemove_impl(SPLPEItem const* lpeitem) @@ -1494,6 +1528,7 @@ void Effect::doOnRemove_impl(SPLPEItem const* lpeitem) } doOnRemove(sp_lpe_item); getLPEObj()->deleted = true; + reloadKnots(sp_lpe_item); } /** @@ -1542,6 +1577,22 @@ void Effect::doOnApply_impl(SPLPEItem const* lpeitem) void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast(lpeitem); + is_current = sp_lpe_item->getCurrentLPE() == this; + // enure is not deleted if we we resurrect the item with the LPE + getLPEObj()->deleted = false; + if (_provides_path_adjustment) { + // By the moment we not handle LPEItem groups. see here how to add to + // https://pastebin.com/e5AesES9 + LPEItemShapesNumbers lpenumbers; + lpenumbers.nchildshapes = 0; + lpenumbers.nsubpaths = pathvector_before_effect.size(); + lpenumbers.ncurves = count_pathvector_curves(pathvector_before_effect); + if (!is_load && lpenumbers != _lpenumbers) { + adjustForNewPath(); + } + //std::cout << _lpenumbers << std::endl; + _lpenumbers = lpenumbers; + } doBeforeEffect(lpeitem); if (is_load) { update_satellites(); @@ -1698,7 +1749,14 @@ Effect::registerParameter(Parameter * param) void Effect::addHandles(KnotHolder *knotholder, SPItem *item) { using namespace Inkscape::LivePathEffect; - + if (getLPEObj()->deleted) { + return; + } + if (is_load) { + if (auto lpeitem = cast(item)) { + sp_lpe_item_update_patheffect(lpeitem, false, false); + } + } // add handles provided by the effect itself addKnotHolderEntities(knotholder, item); @@ -1706,12 +1764,6 @@ Effect::addHandles(KnotHolder *knotholder, SPItem *item) { for (auto & p : param_vector) { p->addKnotHolderEntities(knotholder, item); } - if (is_load) { - auto lpeitem = cast(item); - if (lpeitem) { - sp_lpe_item_update_patheffect(lpeitem, false, false); - } - } } /** @@ -1753,10 +1805,8 @@ Effect::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector(desktop->getTool()); - if (nt) { + if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { + if (auto const nt = dynamic_cast(desktop->getTool())) { Inkscape::UI::Tools::sp_update_helperpath(desktop); } } diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 8ceecfba70368104cf9b89e55d67ea43fd67596b..236180f8d86520e9ab7ced19c1503b4d5ea6c60b 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -55,6 +55,25 @@ enum LPEAction LPE_UPDATE }; +// maybe can grow in the future with more SPLPEItem data +struct LPEItemShapesNumbers { + size_t nchildshapes = 0; + size_t nsubpaths = 0; + size_t ncurves = 0; + inline bool operator==(LPEItemShapesNumbers const &other) const = default; + inline bool operator!=(LPEItemShapesNumbers other) const + { + return !(*this == other); + } +}; +inline std::ostream &operator<<(std::ostream &os, LPEItemShapesNumbers const &lpeitemnumbers) { + os << "Number of child shapes:" << lpeitemnumbers.nchildshapes << std::endl; + os << "Number of subpaths:" << lpeitemnumbers.nsubpaths << std::endl; + os << "Number of curves:" << lpeitemnumbers.ncurves; + return os; +} + + class Effect { public: static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj); @@ -85,6 +104,8 @@ public: void update_satellites(); virtual void doOnException(SPLPEItem const *lpeitem); virtual void doOnVisibilityToggled(SPLPEItem const* lpeitem); + virtual void adjustForNewPath(bool removed = false); + void reloadKnots(SPLPEItem const *lpeitem); void writeParamsToSVG(); std::vector effect_get_satellites(bool force = true); virtual void acceptParamPath (SPPath const* param_path); @@ -143,6 +164,7 @@ public: bool keep_paths; // set this to false allow retain extra generated objects, see measure line LPE bool is_load; bool is_applied; + bool is_current = false; bool on_remove_all; bool refresh_widgets; bool finishiddle = false; @@ -166,6 +188,7 @@ protected: Effect(LivePathEffectObject *lpeobject); friend class SatelliteArrayParam; friend class LPEMeasureSegments; + bool _adjust_path = false; // provide a set of doEffect functions so the developer has a choice // of what kind of input/output parameters he desires. // the order in which they appear is the order in which they are @@ -184,6 +207,7 @@ protected: virtual void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector &hp_vec); bool _provides_knotholder_entities; + bool _provides_path_adjustment = false; LPEAction _lpe_action = LPE_NONE; int oncanvasedit_it; bool show_orig_path; // set this to true in derived effects to automatically have the original @@ -196,6 +220,7 @@ protected: Inkscape::UI::Widget::Registry wr; private: LivePathEffectObject *lpeobj; + LPEItemShapesNumbers _lpenumbers; virtual void transform_multiply(Geom::Affine const &postmul, bool set); virtual bool doOnOpen(SPLPEItem const *lpeitem); virtual void doAfterEffect (SPLPEItem const* lpeitem, SPCurve *curve); diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp index 43676fc836c059a54a7210b05877ab7b83db4ecf..37761e454adb61120ea6ad05feb1a6f2536b72e6 100644 --- a/src/live_effects/lpe-fillet-chamfer.cpp +++ b/src/live_effects/lpe-fillet-chamfer.cpp @@ -28,7 +28,7 @@ #include namespace Inkscape::LivePathEffect { - +static std::vector > removedsatellites = {}; static const Util::EnumData FilletmethodData[] = { { FM_AUTO, N_("Auto"), "auto" }, { FM_ARC, N_("Force arc"), "arc" }, @@ -88,6 +88,7 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) chamfer_steps.param_set_increments(1, 1); chamfer_steps.param_make_integer(); _provides_knotholder_entities = true; + _provides_path_adjustment = true; helperpath = false; previous_unit = Glib::ustring(""); } @@ -100,6 +101,7 @@ void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem) g_warning("LPE Fillet/Chamfer can only be applied to shapes (not groups)."); SPLPEItem *item = const_cast(lpeItem); item->removeCurrentPathEffect(false); + return; } auto rect = cast(splpeitem); Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(shape->curve()->get_pathvector()); @@ -150,8 +152,9 @@ void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem) nodesatellite.setIsTime(flexible); nodesatellite.setHasMirror(true); nodesatellite.setHidden(hide_knots); - _pathvector_nodesatellites->recalculateForNewPathVector(pathv, nodesatellite); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites); + _pathvector_nodesatellites->setDefaultNodeSatellite(nodesatellite); + _pathvector_nodesatellites->adjustForNewPath(pathv); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true, true); } static void set_entry_width_chars(UI::Widget::Scalar &scalar, int const width_chars) @@ -243,7 +246,7 @@ void LPEFilletChamfer::updateAmount() } _pathvector_nodesatellites->updateAmount(power, apply_no_radius, apply_with_radius, only_selected, use_knot_distance, flexible); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true, true); } void LPEFilletChamfer::updateChamferSteps() @@ -253,7 +256,7 @@ void LPEFilletChamfer::updateChamferSteps() } setSelected(_pathvector_nodesatellites); _pathvector_nodesatellites->updateSteps(chamfer_steps, apply_no_radius, apply_with_radius, only_selected); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true, true); } void LPEFilletChamfer::updateNodeSatelliteType(NodeSatelliteType nodesatellitetype) @@ -267,7 +270,7 @@ void LPEFilletChamfer::updateNodeSatelliteType(NodeSatelliteType nodesatellitety setSelected(_pathvector_nodesatellites); _pathvector_nodesatellites->updateNodeSatelliteType(nodesatellitetype, apply_no_radius, apply_with_radius, only_selected); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true, true); } void LPEFilletChamfer::setSelected(PathVectorNodeSatellites *_pathvector_nodesatellites) @@ -285,6 +288,12 @@ void LPEFilletChamfer::setSelected(PathVectorNodeSatellites *_pathvector_nodesat NodeSatellites nodesatellites = _pathvector_nodesatellites->getNodeSatellites(); for (size_t i = 0; i < nodesatellites.size(); ++i) { for (size_t j = 0; j < nodesatellites[i].size(); ++j) { + if (pathv.size() <= i || j >= count_path_curves(pathv[i])) { + // we are on the end of a open path + // for the moment we dont want to use + // this nodesatellite so simplest do nothing with it + continue; + } Geom::Curve const &curve_in = pathv[i][j]; if (only_selected && isNodePointSelected(curve_in.initialPoint()) ){ nodesatellites[i][j].setSelected(true); @@ -301,17 +310,31 @@ void LPEFilletChamfer::setSelected(PathVectorNodeSatellites *_pathvector_nodesat void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem) { if (!pathvector_before_effect.empty()) { - //fillet chamfer specific calls nodesatellites_param.setUseDistance(use_knot_distance); nodesatellites_param.setCurrentZoom(current_zoom); //mandatory call nodesatellites_param.setEffectType(effectType()); - Geom::PathVector const pathv = pathv_to_linear_and_cubic_beziers(pathvector_before_effect); NodeSatellites nodesatellites = nodesatellites_param.data(); - if (nodesatellites.empty()) { + if (lpeItem && nodesatellites.empty()) { doOnApply(lpeItem); // dont want _impl to not update versioning nodesatellites = nodesatellites_param.data(); } + bool updated = _adjust_path; + if (!removedsatellites.empty()) { + Geom::PathVector base = pathvector_before_effect; + for (auto ns : removedsatellites) { + for (size_t i = 0; i < ns.second.size(); ++i) { + nodesatellites.push_back(ns.second[i]); + base.push_back(ns.first[i]); + } + } + _pathvector_nodesatellites->setPathVector(base); + _pathvector_nodesatellites->setNodeSatellites(nodesatellites); + removedsatellites.clear(); + _adjust_path = true; + updated = true; + } + Geom::PathVector const pathv = pathv_to_linear_and_cubic_beziers(pathvector_before_effect); for (size_t i = 0; i < nodesatellites.size(); ++i) { for (size_t j = 0; j < nodesatellites[i].size(); ++j) { if (pathv.size() <= i || j >= count_path_curves(pathv[i])) { @@ -331,56 +354,56 @@ void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem) double size = arcLengthAt(amount, curve_in); nodesatellites[i][j].amount = size; } + updated = true; } - nodesatellites[i][j].hidden = hide_knots; - if (only_selected && isNodePointSelected(curve_in.initialPoint()) ){ + if (nodesatellites[i][j].hidden != hide_knots) { + updated = true; + nodesatellites[i][j].hidden = hide_knots; + } + if (!nodesatellites[i][j].selected && only_selected && isNodePointSelected(curve_in.initialPoint()) ){ + updated = true; nodesatellites[i][j].setSelected(true); } } - - if (pathv.size() > i && !pathv[i].closed()) { + if ((nodesatellites[i].front().amount || nodesatellites[i].front().amount) && + pathv.size() > i && !pathv[i].closed()) + { nodesatellites[i].front().amount = 0; nodesatellites[i].back().amount = 0; + updated = true; } } + if (!_pathvector_nodesatellites) { _pathvector_nodesatellites = new PathVectorNodeSatellites(); + updated = true; } - size_t number_nodes = count_pathvector_nodes(pathv); - size_t previous_number_nodes = _pathvector_nodesatellites->getTotalNodeSatellites(); - if (is_load || number_nodes != previous_number_nodes) { - double power = radius; - if (!flexible) { - power = Inkscape::Util::Quantity::convert(power, unit.get_abbreviation(), "px") / getSPDoc()->getDocumentScale()[Geom::X]; - } - NodeSatelliteType nodesatellite_type = FILLET; - std::map gchar_map_to_nodesatellite_type = boost::assign::map_list_of( - "F", FILLET)("IF", INVERSE_FILLET)("C", CHAMFER)("IC", INVERSE_CHAMFER)("KO", INVALID_SATELLITE); - auto mode_str = mode.param_getSVGValue(); - auto it = gchar_map_to_nodesatellite_type.find(mode_str.raw()); - if (it != gchar_map_to_nodesatellite_type.end()) { - nodesatellite_type = it->second; - } - NodeSatellite nodesatellite(nodesatellite_type); - nodesatellite.setSteps(chamfer_steps); - nodesatellite.setAmount(power); - nodesatellite.setIsTime(flexible); - nodesatellite.setHasMirror(true); - nodesatellite.setHidden(hide_knots); - _pathvector_nodesatellites->setNodeSatellites(nodesatellites); - _pathvector_nodesatellites->recalculateForNewPathVector(pathv, nodesatellite); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true); - nodesatellites_param.reloadKnots(); + _pathvector_nodesatellites->setNodeSatellites(nodesatellites); + if (_adjust_path) { + _adjust_path = false; + auto S = nodesatellites[0].front(); + S.amount = 0; + _pathvector_nodesatellites->setDefaultNodeSatellite(S); + _pathvector_nodesatellites->adjustForNewPath(pathv); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, true, updated); + reloadKnots(lpeItem); } else { _pathvector_nodesatellites->setPathVector(pathv); - _pathvector_nodesatellites->setNodeSatellites(nodesatellites); - nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, false); + nodesatellites_param.setPathVectorNodeSatellites(_pathvector_nodesatellites, updated); } } else { g_warning("LPE Fillet can only be applied to shapes (not groups)."); } } +void +LPEFilletChamfer::adjustForNewPath(bool removed) { + _adjust_path = !removed; + if(removed) { + removedsatellites.emplace_back(pathvector_before_effect, nodesatellites_param.data()); + } +} + void LPEFilletChamfer::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec) { @@ -431,6 +454,10 @@ LPEFilletChamfer::doEffect_path(Geom::PathVector const &path_in) } } size_t tcurves = count_path_curves(pathv[path]); + if (tcurves < 2) { + path_out.push_back(pathv[path]); + continue; + } while (curve_it1 != curve_endit) { ++curve; size_t next_index = curve + 1; diff --git a/src/live_effects/lpe-fillet-chamfer.h b/src/live_effects/lpe-fillet-chamfer.h index 1764334d40185185d57215565526923b80fafd66..0c9d303abdf6568668aa876d1927b6b39d335d25 100644 --- a/src/live_effects/lpe-fillet-chamfer.h +++ b/src/live_effects/lpe-fillet-chamfer.h @@ -38,6 +38,7 @@ public: Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override; void doOnApply(SPLPEItem const *lpeItem) override; Gtk::Widget *newWidget() override; + void adjustForNewPath(bool removed = false) override; Geom::Ray getRay(Geom::Point start, Geom::Point end, Geom::Curve *curve, bool reverse); void addChamferSteps(Geom::Path &tmp_path, Geom::Path path_chamfer, Geom::Point end_arc_point, size_t steps); void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector &hp_vec) override; diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index c37307700cccea22410e7d7834926b974ff92803..cebbe8e79e8d8b55118cda23c4199585da0241d1 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -191,6 +191,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : scale_width.param_set_digits(1); recusion_limit = 0; has_recursion = false; + _provides_path_adjustment = true; } @@ -269,9 +270,9 @@ void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem) } void -LPEPowerStroke::adjustForNewPath() +LPEPowerStroke::adjustForNewPath(bool removed) { - adjust_path = true; + _adjust_path = true; } @@ -596,9 +597,9 @@ LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) } Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(path_in); size_t path_init = 0; - if (adjust_path) { + if (_adjust_path) { path_out_prev.clear(); - adjust_path = false; + _adjust_path = false; // not wait till effect finish Glib::ustring version = lpeversion.param_getSVGValue(); if (version < "1.3") { offset_points.recalculate_controlpoints(pathv[0]); diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index b5e3fd2214d8f42ffa84d3f6e3963e012176faca..b7b87ac07f251b56225873ab96b126e0df861194 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -48,7 +48,7 @@ public: void transform_multiply(Geom::Affine const &postmul, bool set) override; void applyStyle(SPLPEItem *lpeitem); // methods called by path-manipulator upon edits - void adjustForNewPath(); + void adjustForNewPath(bool removed = false) override; PowerStrokePointArrayParam offset_points; BoolParam not_jump; @@ -64,7 +64,6 @@ private: EnumParam end_linecap_type; size_t recusion_limit; bool has_recursion; - bool adjust_path = false; Geom::PathVector path_out_prev; MessageParam message; }; diff --git a/src/live_effects/parameter/nodesatellitesarray.cpp b/src/live_effects/parameter/nodesatellitesarray.cpp index 38b142bc68120edb29a54227a01a4588469adae3..23d7a83a7f85e4787446e4989a80ed5749ff9f20 100644 --- a/src/live_effects/parameter/nodesatellitesarray.cpp +++ b/src/live_effects/parameter/nodesatellitesarray.cpp @@ -23,9 +23,6 @@ #include "live_effects/lpe-fillet-chamfer.h" #include "object/sp-lpe-item.h" #include "ui/dialog/lpe-fillet-chamfer-properties.h" -#include "ui/knot/knot-holder.h" -#include "ui/shape-editor.h" -#include "ui/tools/node-tool.h" // TODO due to internal breakage in glibmm headers, @@ -49,35 +46,22 @@ void NodeSatelliteArrayParam::set_oncanvas_looks(CanvasItemCtrlShape shape, uint _knot_color = color; } -void NodeSatelliteArrayParam::setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, - bool write) +void NodeSatelliteArrayParam::setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, bool update, bool write) { _last_pathvector_nodesatellites = pathVectorNodeSatellites; - if (write) { - param_set_and_write_new_value(_last_pathvector_nodesatellites->getNodeSatellites()); - } else { + if (update || write) { param_setValue(_last_pathvector_nodesatellites->getNodeSatellites()); } + if (write) { + write_to_SVG(); + } } -void NodeSatelliteArrayParam::reloadKnots() +void NodeSatelliteArrayParam::write_to_SVG() { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop && !_global_knot_hide) { - Inkscape::UI::Tools::NodeTool *nt = dynamic_cast(desktop->getTool()); - if (nt) { - for (auto &_shape_editor : nt->_shape_editors) { - Inkscape::UI::ShapeEditor *shape_editor = _shape_editor.second.get(); - if (shape_editor && shape_editor->lpeknotholder) { - SPItem *item = shape_editor->lpeknotholder->item; - delete shape_editor->lpeknotholder; - shape_editor->lpeknotholder = nullptr; - shape_editor->set_item(item); - } - } - } - } + param_set_and_write_new_value(_last_pathvector_nodesatellites->getNodeSatellites()); } + void NodeSatelliteArrayParam::setUseDistance(bool use_knot_distance) { _use_distance = use_knot_distance; @@ -360,6 +344,7 @@ FilletChamferKnotHolderEntity::knot_ungrabbed(Geom::Point const &p, Geom::Point Inkscape::LivePathEffect::LPEFilletChamfer *filletchamfer = dynamic_cast(_pparam->param_effect); if (filletchamfer) { filletchamfer->helperpath = false; + _pparam->write_to_SVG(); filletchamfer->makeUndoDone(_("Move handle")); } } @@ -385,7 +370,7 @@ Geom::Point FilletChamferKnotHolderEntity::knot_get() const } NodeSatellite nodesatellite = _pparam->_vector[satelite_index][subsatelite_index]; Geom::PathVector pathv = _pparam->_last_pathvector_nodesatellites->getPathVector(); - if (nodesatellite.hidden || + if (nodesatellite.hidden || pathv.size() <= satelite_index || (!pathv[satelite_index].closed() && (subsatelite_index == 0 || subsatelite_index == count_path_nodes(pathv[satelite_index]) - 1))) // ignore first and last nodesatellites on open paths diff --git a/src/live_effects/parameter/nodesatellitesarray.h b/src/live_effects/parameter/nodesatellitesarray.h index 49855db2286a0f5cac61c87e761e13a61a8b7afe..fddec766bdd889e2acfc16ed4b08ce6061a7885d 100644 --- a/src/live_effects/parameter/nodesatellitesarray.h +++ b/src/live_effects/parameter/nodesatellitesarray.h @@ -57,9 +57,9 @@ public: void setCurrentZoom(double current_zoom); void setGlobalKnotHide(bool global_knot_hide); void setEffectType(EffectType et); - void reloadKnots(); + void write_to_SVG(); void updateAmmount(double amount); - void setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, bool write = true); + void setPathVectorNodeSatellites(PathVectorNodeSatellites *pathVectorNodeSatellites, bool update = true, bool write = false); void set_oncanvas_looks(Inkscape::CanvasItemCtrlShape shape, uint32_t color); diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp index 50d28dbdc24b3b1134a0102b72fb50a81a3e76b2..04263b85e271a6da73c2825379a2a3e640470a06 100755 --- a/src/object/sp-lpe-item.cpp +++ b/src/object/sp-lpe-item.cpp @@ -275,16 +275,24 @@ bool SPLPEItem::performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape: current->bbox_vis_cache_is_valid = false; current->bbox_geom_cache_is_valid = false; } - auto group = cast(this); - if (!group && !is_clip_or_mask) { - lpe->doBeforeEffect_impl(this); - } - + // wrap all LPE into a try catch + // because all logic is same process try { + auto group = cast(this); + if (!group && !is_clip_or_mask) { + lpe->doBeforeEffect_impl(this); + } lpe->doEffect(curve); + if (!group) { + // To have processed the shape to doAfterEffect + current->setCurveInsync(curve); + if (curve) { + lpe->pathvector_after_effect = curve->get_pathvector(); + } + lpe->doAfterEffect_impl(this, curve); + } lpe->has_exception = false; } - catch (std::exception & e) { g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what()); if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) { @@ -294,15 +302,6 @@ bool SPLPEItem::performOnePathEffect(SPCurve *curve, SPShape *current, Inkscape: lpe->doOnException(this); return false; } - - if (!group) { - // To have processed the shape to doAfterEffect - current->setCurveInsync(curve); - if (curve) { - lpe->pathvector_after_effect = curve->get_pathvector(); - } - lpe->doAfterEffect_impl(this, curve); - } } } return true; @@ -1251,19 +1250,10 @@ SPLPEItem::applyToClipPathOrMask(SPItem *clip_mask, SPItem* to, Inkscape::LivePa if (shape->curve()) { auto c = *shape->curve(); bool success = false; - try { - if (lpe) { - success = this->performOnePathEffect(&c, shape, lpe, true); - } else { - success = this->performPathEffect(&c, shape, true); - } - } catch (std::exception & e) { - g_warning("Exception during LPE execution. \n %s", e.what()); - if (SP_ACTIVE_DESKTOP && SP_ACTIVE_DESKTOP->messageStack()) { - SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE, - _("An exception occurred during execution of the Path Effect.") ); - } - success = false; + if (lpe) { + success = this->performOnePathEffect(&c, shape, lpe, true); + } else { + success = this->performPathEffect(&c, shape, true); } if (success) { auto str = sp_svg_write_path(c.get_pathvector()); diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 0da6d5c4343e57763dccf99d57e5479ddd52695b..58387ea1ceafe7fca186d03d731ac9e1cd5f207d 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -29,6 +29,8 @@ #include "display/curve.h" +#include "live_effects/effect.h" + #include "object/box3d.h" #include "object/object-set.h" #include "object/sp-flowtext.h" @@ -104,8 +106,17 @@ void ObjectSet::combine(bool skip_undo, bool silent) } items_copy = sp_degroup_list(items_copy); // converting to path may have added more groups, descend again - - std::sort(items_copy.begin(), items_copy.end(), [] (auto a, auto b) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool keep_corners_join = prefs->getBool("/tools/nodes/keepcornersjoin", true); + + std::sort(items_copy.begin(), items_copy.end(), [keep_corners_join] (auto a, auto b) { + if (keep_corners_join) { + if (auto path = cast((b))) { + if(path->hasPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER)){ + return true; + } + } + } return sp_repr_compare_position(a->getRepr(), b->getRepr()) < 0; }); assert(!items_copy.empty()); // cannot be empty because of check at top of function @@ -152,6 +163,9 @@ void ObjectSet::combine(bool skip_undo, bool silent) if (item->getRepr()->parent() == parent) { position--; } + for (auto lpe : path->getPathEffects()) { // notify deleted elements + lpe->adjustForNewPath(true); + } // delete the object for real, so that its clones can take appropriate action item->deleteObject(); } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 708d438948a0edbcbda12a5527eb0926f3e07f1a..9b2c3fe9d95272d240be34e2c17922177c76c1db 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -2830,6 +2830,10 @@ void InkscapePreferences::initPageBehavior() _lpe_copy_mirroricons.init ( _("Add advanced tiling options"), "/live_effects/copy/mirroricons", true); // text label _page_lpe.add_line( true, "", _lpe_copy_mirroricons, "", _("Enables using 16 advanced mirror options between the copies (so there can be copies that are mirrored differently between the rows and the columns) for Tiling LPE")); // tooltip + _page_lpe.add_group_header( _("Corners")); + _lpe_keep_corners_join.init ( _("Keep on joins"), "/tools/nodes/keepcornersjoin", true); // text label + _page_lpe.add_line( true, "", _lpe_keep_corners_join, "", + _("Allow corners live path effect preserve always on join with diferent items")); // tooltip this->AddPage(_page_lpe, _("Live Path Effects (LPE)"), iter_behavior, PREFS_PAGE_BEHAVIOR_LPE); } diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 41babd679dacb95616b1952a3a0757e8cce4af07..fc1b64b0aaaee74d335faaf87630a55aaf5894f4 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -408,6 +408,7 @@ protected: UI::Widget::PrefCheckButton _cleanup_swatches; UI::Widget::PrefCheckButton _lpe_copy_mirroricons; + UI::Widget::PrefCheckButton _lpe_keep_corners_join; UI::Widget::PrefCheckButton _lpe_show_experimental; UI::Widget::PrefSpinButton _importexport_export_res; diff --git a/src/ui/knot/knot-holder.cpp b/src/ui/knot/knot-holder.cpp index 5154c02d9c7a3579d5a8618a893598be849f0bdf..147246c84492c83d3cd24d81b2a9f4849820536a 100644 --- a/src/ui/knot/knot-holder.cpp +++ b/src/ui/knot/knot-holder.cpp @@ -218,21 +218,11 @@ KnotHolder::transform_selected(Geom::Affine transform){ } void -KnotHolder::unselect_knots(){ - Inkscape::UI::Tools::NodeTool *nt = dynamic_cast(desktop->getTool()); - if (nt) { - for (auto &_shape_editor : nt->_shape_editors) { - Inkscape::UI::ShapeEditor *shape_editor = _shape_editor.second.get(); - if (shape_editor && shape_editor->has_knotholder()) { - KnotHolder * knotholder = shape_editor->knotholder; - if (knotholder) { - for (auto e : knotholder->entity) { - if (e->knot->is_selected()) { - e->knot->selectKnot(false); - } - } - } - } +KnotHolder::unselect_knots() { + for (auto i : entity) { + SPKnot *knot = i->knot; + if (knot->is_selected()) { + knot->selectKnot(false); } } } @@ -295,7 +285,7 @@ KnotHolder::knot_ungrabbed_handler(SPKnot *knot, guint state) for(auto e : this->entity) { if (e->knot == knot) { e->knot_ungrabbed(e->knot->position(), e->knot->drag_origin * item->i2dt_affine().inverse() * _edit_transform.inverse(), state); - if (e->knot->is_lpe) { + if (knot->is_lpe) { return; } break; diff --git a/src/ui/shape-editor.cpp b/src/ui/shape-editor.cpp index fe8e2a745a3fc54479fddf5ac5e57e81f365ed9d..9a5b477173d09d20aac543377ce2eef36c8129c9 100644 --- a/src/ui/shape-editor.cpp +++ b/src/ui/shape-editor.cpp @@ -15,6 +15,7 @@ #include "shape-editor.h" #include "desktop.h" +#include "inkscape.h" #include "document.h" #include "live_effects/effect.h" #include "object/sp-lpe-item.h" @@ -124,19 +125,11 @@ void ShapeEditor::set_item(SPItem *item) { if (item) { Inkscape::XML::Node *repr; + _externChangedConn = INKSCAPE.signal_external_change.connect([this] () { this->reset_item(); }); if (!this->knotholder) { // only recreate knotholder if none is present this->knotholder = createKnotHolder(item, desktop, _edit_rotation, _edit_marker_mode); } - auto lpe = cast(item); - if (!(lpe && - lpe->getCurrentLPE() && - lpe->getCurrentLPE()->isVisible() && - lpe->getCurrentLPE()->providesKnotholder())) - { - delete this->lpeknotholder; - this->lpeknotholder = nullptr; - } if (!this->lpeknotholder) { // only recreate knotholder if none is present this->lpeknotholder = createLPEKnotHolder(item, desktop); @@ -172,11 +165,11 @@ void ShapeEditor::set_item(SPItem *item) { Why not make a reload function in KnotHolder? */ void ShapeEditor::reset_item() { - if (knotholder) { + if (knotholder || lpeknotholder) { SPObject *obj = desktop->getDocument()->getObjectByRepr(knotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. - set_item(cast(obj)); - } else if (lpeknotholder) { - SPObject *obj = desktop->getDocument()->getObjectByRepr(lpeknotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. + if (!obj) { + obj = desktop->getDocument()->getObjectByRepr(lpeknotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. + } set_item(cast(obj)); } } diff --git a/src/ui/shape-editor.h b/src/ui/shape-editor.h index fdaefe9b71c3206a542e01bfc9a349894cc7d7da..5d97288dec710de62329ecf9d1482a5af671b9c3 100644 --- a/src/ui/shape-editor.h +++ b/src/ui/shape-editor.h @@ -15,6 +15,7 @@ #include <2geom/affine.h> +#include "helper/auto-connection.h" #include "xml/node-observer.h" class KnotHolder; @@ -49,6 +50,7 @@ private: void reset_item(); static bool _blockSetItem; + Inkscape::auto_connection _externChangedConn; SPDesktop *desktop; Inkscape::XML::Node *knotholder_listener_attached_for{nullptr}; Inkscape::XML::Node *lpeknotholder_listener_attached_for{nullptr}; diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index b854c1d7fa03a93fd8e6684e1088fd8a0a8b4cf3..c9fc1303f5696d7038805eb9da12dfe1734ae3f5 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -84,6 +84,31 @@ void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs) } } +// force corners LPE keep when join with non corners path +// anyway not fix bsplie or spiro, mainy for this reason both require a tool based specialiced original-d +// but power stroke or others can benefit also (but in a moment we need to define a rule +// of preferences if both paths have different LPE +// for the moment I keep simple just for corners LPE +bool fix_corners(IterPair &join, std::map> mmap, bool keep_corners_join) +{ + if (keep_corners_join) { + keep_corners_join = false; + for (auto & i : mmap) { + if (join.second == (i.second.get())->extremeNode(join.second, true, true, true)) { + if (auto path = cast((i.second.get())->item())) { + if(path->hasPathEffectOfType(Inkscape::LivePathEffect::FILLET_CHAMFER)){ + std::swap(join.first, join.second); + return true; + } + } + } + + } + } + return false; +} + + /** After this function, first should be at the end of path and second at the beginning. * @returns True if the nodes are in the same subpath */ bool prepare_join(IterPair &join_iters) @@ -376,8 +401,12 @@ void MultiPathManipulator::joinNodes() } find_join_iterators(_selection, joins); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool keep_corners_join = prefs->getBool("/tools/nodes/keepcornersjoin", true); + for (auto & join : joins) { bool same_path = prepare_join(join); + bool corners_fix = same_path ? false : fix_corners(join, _mmap, keep_corners_join); NodeList &sp_first = NodeList::get(join.first); NodeList &sp_second = NodeList::get(join.second); join.first->setType(NODE_CUSP, false); @@ -413,6 +442,9 @@ void MultiPathManipulator::joinNodes() } else { sp_first.splice(sp_first.end(), sp_second); sp_second.kill(); + if (corners_fix) { + sp_first.reverse(); + } } _selection.insert(join.first.ptr()); } @@ -449,9 +481,12 @@ void MultiPathManipulator::joinSegments() if (_selection.empty()) return; IterPairList joins; find_join_iterators(_selection, joins); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool keep_corners_join = prefs->getBool("/tools/nodes/keepcornersjoin", true); for (auto & join : joins) { bool same_path = prepare_join(join); + bool corners_fix = same_path ? false : fix_corners(join, _mmap, keep_corners_join); NodeList &sp_first = NodeList::get(join.first); NodeList &sp_second = NodeList::get(join.second); join.first->setType(NODE_CUSP, false); @@ -461,6 +496,9 @@ void MultiPathManipulator::joinSegments() } else { sp_first.splice(sp_first.end(), sp_second); sp_second.kill(); + if (corners_fix) { + sp_first.reverse(); + } } } diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index d48234e696b18d4f95e14d6732e13b004fbd6dea..02a5da0f8dc40ff674620d5f786ba38736a103fa 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -56,7 +56,6 @@ public: void insertNodesAtExtrema(ExtremumType extremum); void insertNodes(); void insertNode(Geom::Point pt); - void alertLPE(); void duplicateNodes(); void copySelectedPath(Geom::PathBuilder *builder); void joinNodes(); diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 64b0143f79f9d5511e5f166381bd330d4f4561fd..a7fb7652049402f80f19c9845c30930a8bab27f3 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -14,6 +14,7 @@ #include <2geom/bezier-utils.h> #include <2geom/path-sink.h> #include <2geom/point.h> +#include #include #include @@ -25,7 +26,6 @@ #include "helper/geom.h" #include "live_effects/lpeobject.h" -#include "live_effects/lpe-powerstroke.h" #include "live_effects/lpe-bspline.h" #include "live_effects/parameter/path.h" @@ -1464,7 +1464,13 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) ++i; } } + auto path = cast(_path); if (pathv.empty()) { + if (alert_LPE && path) { + for (auto lpe : path->getPathEffects()) { // first notify erased elements + lpe->adjustForNewPath(true); + } + } return; } @@ -1472,16 +1478,9 @@ void PathManipulator::_createGeometryFromControlPoints(bool alert_LPE) return; } _spcurve = SPCurve(pathv); - if (alert_LPE) { - /// \todo note that _path can be an Inkscape::LivePathEffect::Effect* too, kind of confusing, rework member naming? - auto path = cast(_path); - if (path && path->hasPathEffect()) { - Inkscape::LivePathEffect::Effect *this_effect = - path->getFirstPathEffectOfType(Inkscape::LivePathEffect::POWERSTROKE); - LivePathEffect::LPEPowerStroke *lpe_pwr = dynamic_cast(this_effect); - if (lpe_pwr) { - lpe_pwr->adjustForNewPath(); - } + if (alert_LPE && path) { + for (auto lpe : path->getPathEffects()) { // notify created elements + lpe->adjustForNewPath(false); } } if (_live_outline) { diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index adb6f56a7a9969fb503832d9fbcd52ef7ff0bf10..3923dce4bbc9f7a4f77057ab1aaa35a507dac7a3 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -384,6 +384,18 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { auto item = cast(r.object); si->set_item(item); this->_shape_editors.insert({item, std::move(si)}); + // delete all knotholders on remove item become recreated later + _releaseConnections[item] = item->connectRelease([this] (auto item) { + for (auto i = this->_shape_editors.begin(); i != this->_shape_editors.end();) { + if (item == (*i).first) { + (*i).second->unset_item(); + this->_shape_editors.erase(i++); + } else { + ++i; + } + + } + }); } } diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h index aa4c2571ec0d5f194106a4e3fda0fa89dd334698..0c4c0230abb425aea2e6ad18bcbc893f7f849650 100644 --- a/src/ui/tools/node-tool.h +++ b/src/ui/tools/node-tool.h @@ -14,9 +14,10 @@ #include #include -#include #include +#include +#include "helper/auto-connection.h" #include "ui/tools/tool-base.h" namespace Inkscape { @@ -63,6 +64,7 @@ public: private: Inkscape::Rubberband *get_rubberband() const; + std::unordered_map _releaseConnections; sigc::connection _selection_changed_connection; sigc::connection _mouseover_changed_connection;