diff --git a/libs/image/kis_base_node.h b/libs/image/kis_base_node.h --- a/libs/image/kis_base_node.h +++ b/libs/image/kis_base_node.h @@ -263,7 +263,14 @@ * Return all the properties of this layer as a KoProperties-based * serializable key-value list. */ - KoProperties & nodeProperties() const; + const KoProperties & nodeProperties() const; + + /** + * Set a node property. + * @param name name of the property to be set. + * @param value value to set the property to. + */ + void setNodeProperty(const QString & name, const QVariant & value); /** * Merge the specified properties with the properties of this diff --git a/libs/image/kis_base_node.cpp b/libs/image/kis_base_node.cpp --- a/libs/image/kis_base_node.cpp +++ b/libs/image/kis_base_node.cpp @@ -132,7 +132,7 @@ if (opacity() == val) return; - nodeProperties().setProperty("opacity", val); + setNodeProperty("opacity", val); baseNodeChangedCallback(); baseNodeInvalidateAllFramesCallback(); @@ -177,11 +177,17 @@ setUserLocked(properties.at(1).state.toBool()); } -KoProperties & KisBaseNode::nodeProperties() const +const KoProperties & KisBaseNode::nodeProperties() const { return m_d->properties; } +void KisBaseNode::setNodeProperty(const QString & name, const QVariant & value) +{ + m_d->properties.setProperty(name, value); + baseNodeChangedCallback(); +} + void KisBaseNode::mergeNodeProperties(const KoProperties & properties) { QMapIterator iter = properties.propertyIterator(); diff --git a/libs/image/kis_layer.h b/libs/image/kis_layer.h --- a/libs/image/kis_layer.h +++ b/libs/image/kis_layer.h @@ -224,6 +224,11 @@ */ void updateClones(const QRect &rect); + /** + * Informs this layers that its masks might have changed. + */ + void notifyChildMaskChanged(KisNodeSP changedChildMask); + public: qint32 x() const override; qint32 y() const override; @@ -256,7 +261,12 @@ /** * @return the list of effect masks */ - QList effectMasks(KisNodeSP lastNode = KisNodeSP()) const; + const QList &effectMasks() const; + + /** + * @return the list of effect masks up to a certain node + */ + QList effectMasks(KisNodeSP lastNode) const; /** * Get the group layer that contains this layer. @@ -272,6 +282,8 @@ // override from KisNode QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const override; + void childNodeChanged(KisNodeSP changedChildNode) override; + protected: /** @@ -371,6 +383,13 @@ KisNodeSP filthyNode, KisNodeSP lastNode) const; bool canMergeAndKeepBlendOptions(KisLayerSP otherLayer); + + void updateSelectionMask(); + + void updateEffectMasks(); + + QList searchEffectMasks(KisNodeSP lastNode) const; + private: friend class KisLayerProjectionPlane; friend class KisTransformMask; diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc --- a/libs/image/kis_layer.cc +++ b/libs/image/kis_layer.cc @@ -131,6 +131,9 @@ KisAbstractProjectionPlaneSP layerStyleProjectionPlane; KisAbstractProjectionPlaneSP projectionPlane; + + KisSelectionMaskSP selectionMask; + QList effectMasks; }; @@ -143,6 +146,7 @@ m_d->image = image; m_d->metaDataStore = new KisMetaData::Store(); m_d->projectionPlane = toQShared(new KisLayerProjectionPlane(this)); + notifyChildMaskChanged(KisNodeSP()); } KisLayer::KisLayer(const KisLayer& rhs) @@ -160,6 +164,7 @@ if (rhs.m_d->layerStyle) { setLayerStyle(rhs.m_d->layerStyle->clone()); } + notifyChildMaskChanged(KisNodeSP()); } } @@ -313,7 +318,7 @@ void KisLayer::setTemporary(bool t) { - nodeProperties().setProperty("temporary", t); + setNodeProperty("temporary", t); } KisImageWSP KisLayer::image() const @@ -435,8 +440,19 @@ m_d->clonesList.setDirty(rect); } +void KisLayer::notifyChildMaskChanged(KisNodeSP changedChildMask) +{ + updateSelectionMask(); + updateEffectMasks(); +} + KisSelectionMaskSP KisLayer::selectionMask() const { + return m_d->selectionMask; +} + +void KisLayer::updateSelectionMask() +{ KoProperties properties; properties.setProperty("active", true); QList masks = childNodes(QStringList("KisSelectionMask"), properties); @@ -444,10 +460,11 @@ // return the first visible mask Q_FOREACH (KisNodeSP mask, masks) { if (mask->visible()) { - return dynamic_cast(mask.data()); + m_d->selectionMask = dynamic_cast(mask.data()); + return; } } - return KisSelectionMaskSP(); + m_d->selectionMask = KisSelectionMaskSP(); } KisSelectionSP KisLayer::selection() const @@ -468,8 +485,28 @@ /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// +const QList &KisLayer::effectMasks() const +{ + return m_d->effectMasks; +} + QList KisLayer::effectMasks(KisNodeSP lastNode) const { + if (lastNode.isNull()) { + return effectMasks(); + } else { + // happens rarely. + return searchEffectMasks(lastNode); + } +} + +void KisLayer::updateEffectMasks() +{ + m_d->effectMasks = searchEffectMasks(KisNodeSP()); +} + +QList KisLayer::searchEffectMasks(KisNodeSP lastNode) const +{ QList masks; if (childCount() > 0) { @@ -477,7 +514,7 @@ properties.setProperty("visible", true); QList nodes = childNodes(QStringList("KisEffectMask"), properties); - Q_FOREACH (const KisNodeSP& node, nodes) { + Q_FOREACH (const KisNodeSP& node, nodes) { if (node == lastNode) break; KisEffectMaskSP mask = dynamic_cast(const_cast(node.data())); @@ -485,22 +522,13 @@ masks.append(mask); } } + return masks; } bool KisLayer::hasEffectMasks() const { - if (childCount() == 0) return false; - - KisNodeSP node = firstChild(); - while (node) { - if (node->inherits("KisEffectMask") && node->visible()) { - return true; - } - node = node->nextSibling(); - } - - return false; + return !m_d->effectMasks.empty(); } QRect KisLayer::masksChangeRect(const QList &masks, @@ -784,6 +812,13 @@ return changeRect; } +void KisLayer::childNodeChanged(KisNodeSP changedChildNode) +{ + if (dynamic_cast(changedChildNode.data())) { + notifyChildMaskChanged(changedChildNode); + } +} + QRect KisLayer::incomingChangeRect(const QRect &rect) const { return rect; diff --git a/libs/image/kis_mask.h b/libs/image/kis_mask.h --- a/libs/image/kis_mask.h +++ b/libs/image/kis_mask.h @@ -204,6 +204,8 @@ KisKeyframeChannel *requestKeyframeChannel(const QString &id) override; + void baseNodeChangedCallback() override; + private: friend class KisMaskProjectionPlane; diff --git a/libs/image/kis_mask.cc b/libs/image/kis_mask.cc --- a/libs/image/kis_mask.cc +++ b/libs/image/kis_mask.cc @@ -410,3 +410,12 @@ return KisNode::requestKeyframeChannel(id); } +void KisMask::baseNodeChangedCallback() +{ + KisNodeSP up = parent(); + KisLayer *layer = dynamic_cast(up.data()); + if (layer) { + layer->notifyChildMaskChanged(this); + } + KisNode::baseNodeChangedCallback(); +} diff --git a/libs/image/kis_node.h b/libs/image/kis_node.h --- a/libs/image/kis_node.h +++ b/libs/image/kis_node.h @@ -232,6 +232,13 @@ */ virtual QRect accessRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const; + /** + * Called each time direct child nodes are added or removed under this + * node as parent. This does not track changes inside the child nodes + * or the child nodes' properties. + */ + virtual void childNodeChanged(KisNodeSP changedChildNode); + public: // Graph methods /** diff --git a/libs/image/kis_node.cpp b/libs/image/kis_node.cpp --- a/libs/image/kis_node.cpp +++ b/libs/image/kis_node.cpp @@ -249,6 +249,10 @@ return rect; } +void KisNode::childNodeChanged(KisNodeSP changedChildNode) +{ +} + KisAbstractProjectionPlaneSP KisNode::projectionPlane() const { KIS_ASSERT_RECOVER_NOOP(0 && "KisNode::projectionPlane() is not defined!"); @@ -501,11 +505,12 @@ newNode->setGraphListener(m_d->graphListener); } + childNodeChanged(newNode); + if (m_d->graphListener) { m_d->graphListener->nodeHasBeenAdded(this, idx); } - return true; } @@ -528,6 +533,8 @@ m_d->nodes.removeAt(index); } + childNodeChanged(removedNode); + if (m_d->graphListener) { m_d->graphListener->nodeHasBeenRemoved(this, index); } diff --git a/libs/image/kis_paint_layer.cc b/libs/image/kis_paint_layer.cc --- a/libs/image/kis_paint_layer.cc +++ b/libs/image/kis_paint_layer.cc @@ -306,7 +306,7 @@ m_d->onionSkinConnection.clear(); } - nodeProperties().setProperty("onionskin", state); + setNodeProperty("onionskin", state); if (m_d->contentChannel) { m_d->contentChannel->setOnionSkinsEnabled(state); diff --git a/libs/image/kis_selection_mask.cpp b/libs/image/kis_selection_mask.cpp --- a/libs/image/kis_selection_mask.cpp +++ b/libs/image/kis_selection_mask.cpp @@ -134,7 +134,7 @@ void KisSelectionMask::setVisible(bool visible, bool isLoading) { - nodeProperties().setProperty("visible", visible); + setNodeProperty("visible", visible); if (!isLoading) { if (selection()) @@ -160,7 +160,7 @@ } } - nodeProperties().setProperty("active", active); + setNodeProperty("active", active); if (image) { image->nodeChanged(this); diff --git a/libs/image/tests/kis_base_node_test.cpp b/libs/image/tests/kis_base_node_test.cpp --- a/libs/image/tests/kis_base_node_test.cpp +++ b/libs/image/tests/kis_base_node_test.cpp @@ -98,7 +98,7 @@ props.setProperty("locked", true); QVERIFY(!node->check(props)); - node->nodeProperties().setProperty("locked", false); + node->setNodeProperty("locked", false); QVERIFY(node->userLocked() == false); } {