R: [calligra] /: FEATURE: Isolated Mode for Krita nodes

silvio grosso grossosilvio at yahoo.it
Thu May 23 17:45:48 UTC 2013


Hi Dmitry,

> FEATURE: Isolated Mode for Krita nodes

Quite Interesting feature indeed :-)

My best regards,

Silvio Grosso

--- Gio 23/5/13, Dmitry Kazakov <dimula73 at gmail.com> ha scritto:

Da: Dmitry Kazakov <dimula73 at gmail.com>
Oggetto: [calligra] /: FEATURE: Isolated Mode for Krita nodes
A: kde-commits at kde.org
Cc: kimageshop at kde.org
Data: Giovedì 23 maggio 2013, 18:07

Git commit e1e85dea054bf7718a05a6e63cec83b59ffe0315 by Dmitry Kazakov.
Committed on 23/05/2013 at 18:06.
Pushed by dkazakov into branch 'master'.

FEATURE: Isolated Mode for Krita nodes

Implemented a mode, which allow a user to limit visible layers by
a subtree of a graph. It means you can look at a layer or mask or
a group in isolated environment, without other nodes seen.

To switch on the mode, you can select a context menu item in the
Layers Docker. To switch off, just select another layer or just use
the same context menu.

CCMAIL:kimageshop at kde.org

M  +52   -9    krita/image/kis_image.cc
M  +15   -16   krita/image/kis_image.h
M  +10   -0    krita/image/kis_mask.cc
M  +10   -0    krita/image/kis_mask.h
M  +8    -6    krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M  +1    -1    krita/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
M  +56   -0    krita/ui/kis_node_manager.cpp
M  +4    -0    krita/ui/kis_node_manager.h
M  +45   -2    krita/ui/kis_node_model.cpp
M  +6    -0    krita/ui/kis_node_model.h
M  +2    -2    libs/main/KoDocumentSectionDelegate.cpp
M  +1    -1    libs/main/KoDocumentSectionDelegate.h

http://commits.kde.org/calligra/e1e85dea054bf7718a05a6e63cec83b59ffe0315

diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index 0c5fc22..ad6cceb 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -112,6 +112,7 @@ public:
     KisGroupLayerSP rootLayer; // The layers are contained in here
     QList<KisLayer*> dirtyLayers; // for thumbnails
     QList<KisLayerComposition*> compositions;
+    KisNodeSP isolatedRootNode;
 
     KisNameServer *nserver;
 
@@ -177,6 +178,13 @@ KisImage::~KisImage()
     disconnect(); // in case Qt gets confused
 }
 
+void KisImage::aboutToAddANode(KisNode *parent, int index)
+{
+    KisNodeGraphListener::aboutToAddANode(parent, index);
+    SANITY_CHECK_LOCKED("aboutToAddANode");
+    stopIsolatedMode();
+}
+
 void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
 {
     KisNodeGraphListener::nodeHasBeenAdded(parent, index);
@@ -187,6 +195,7 @@ void KisImage::nodeHasBeenAdded(KisNode *parent, int index)
 
 void KisImage::aboutToRemoveANode(KisNode *parent, int index)
 {
+    stopIsolatedMode();
     KisNodeGraphListener::aboutToRemoveANode(parent, index);
 
     SANITY_CHECK_LOCKED("aboutToRemoveANode");
@@ -807,6 +816,11 @@ KisGroupLayerSP KisImage::rootLayer() const
 
 KisPaintDeviceSP KisImage::projection()
 {
+    if (m_d->isolatedRootNode) {
+        return m_d->isolatedRootNode->projection();
+    }
+
+
     Q_ASSERT(m_d->rootLayer);
     KisPaintDeviceSP projection = m_d->rootLayer->projection();
     Q_ASSERT(projection);
@@ -1059,7 +1073,7 @@ QImage KisImage::convertToQImage(qint32 x,
                                  qint32 h,
                                  const KoColorProfile * profile)
 {
-    KisPaintDeviceSP dev = m_d->rootLayer->projection();
+    KisPaintDeviceSP dev = projection();
     if (!dev) return QImage();
     QImage image = dev->convertToQImage(const_cast<KoColorProfile*>(profile), x, y, w, h,
                                         KoColorConversionTransformation::InternalRenderingIntent,
@@ -1114,7 +1128,7 @@ QImage KisImage::convertToQImage(const QRect& scaledRect, const QSize& scaledIma
     srcRect.setTop(static_cast<int>(scaledRect.top() * yScale));
     srcRect.setBottom(static_cast<int>(ceil((scaledRect.bottom() + 1) * yScale)) - 1);
 
-    KisPaintDeviceSP mergedImage = m_d->rootLayer->projection();
+    KisPaintDeviceSP mergedImage = projection();
     quint8 *scaledImageData = new quint8[scaledRect.width() * scaledRect.height() * pixelSize];
 
     quint8 *imageRow = new quint8[srcRect.width() * pixelSize];
@@ -1173,13 +1187,6 @@ QImage KisImage::convertToQImage(const QRect& scaledRect, const QSize& scaledIma
     return image;
 }
 
-
-KisPaintDeviceSP KisImage::mergedImage()
-{
-    refreshGraph();
-    return m_d->rootLayer->projection();
-}
-
 void KisImage::notifyLayersChanged()
 {
     m_d->signalRouter->emitNotification(LayersChangedSignal);
@@ -1221,6 +1228,8 @@ KisActionRecorder* KisImage::actionRecorder() const
 
 void KisImage::setRootLayer(KisGroupLayerSP rootLayer)
 {
+    stopIsolatedMode();
+
     if (m_d->rootLayer) {
         m_d->rootLayer->setGraphListener(0);
         m_d->rootLayer->disconnect();
@@ -1348,6 +1357,40 @@ KisStrokeId KisImage::startStroke(KisStrokeStrategy *strokeStrategy)
     return id;
 }
 
+void KisImage::startIsolatedMode(KisNodeSP node)
+{
+    barrierLock();
+    unlock();
+
+    m_d->isolatedRootNode = node;
+    emit sigIsolatedModeChanged();
+
+    notifyProjectionUpdated(bounds());
+}
+
+void KisImage::stopIsolatedMode()
+{
+    if (!m_d->isolatedRootNode)  return;
+
+    KisNodeSP oldRootNode = m_d->isolatedRootNode;
+    m_d->isolatedRootNode = 0;
+
+    emit sigIsolatedModeChanged();
+
+    notifyProjectionUpdated(bounds());
+
+    // TODO: Substitute notifyProjectionUpdated() with this code
+    // when update optimization is implemented
+    // 
+    // QRect updateRect = bounds() | oldRootNode->extent();
+    // oldRootNode->setDirty(updateRect);
+}
+
+KisNodeSP KisImage::isolatedModeRoot() const
+{
+    return m_d->isolatedRootNode;
+}
+
 void KisImage::addJob(KisStrokeId id, KisStrokeJobData *data)
 {
     if (m_d->scheduler) {
diff --git a/krita/image/kis_image.h b/krita/image/kis_image.h
index 11e17ca..7988df5 100644
--- a/krita/image/kis_image.h
+++ b/krita/image/kis_image.h
@@ -84,6 +84,7 @@ public:
 
 public: // KisNodeGraphListener implementation
 
+    void aboutToAddANode(KisNode *parent, int index);
     void nodeHasBeenAdded(KisNode *parent, int index);
     void aboutToRemoveANode(KisNode *parent, int index);
     void nodeChanged(KisNode * node);
@@ -387,22 +388,6 @@ public:
     }
 
     /**
-     * Starting form 2.3 mergedImage() is declared deprecated.
-     * If you want to get a projection of the image, please use
-     * something like:
-     *
-     * image->lock();
-     * read_something_from_the_image(image->projection());
-     * image->unlock();
-     *
-     * or if you want to get a full refresh of the image graph
-     * performed beforehand (do you really want it?) (sure?) then
-     * you can add a call to image->refreshGraph() before locking
-     * the image.
-     */
-    KDE_DEPRECATED KisPaintDeviceSP mergedImage();
-
-    /**
      * @return the root node of the image node graph
      */
     KisGroupLayerSP rootLayer() const;
@@ -503,6 +488,11 @@ public:
      */
     void removeComposition(KisLayerComposition* composition);
 
+public:
+    void startIsolatedMode(KisNodeSP node);
+    void stopIsolatedMode();
+    KisNodeSP isolatedModeRoot() const;
+
 signals:
 
     /**
@@ -622,6 +612,15 @@ signals:
      */
     void sigStrokeEndRequested();
 
+    /**
+     * Emitted when the isolated mode status has changed.
+     *
+     * Can be used by the recievers to catch a fact of forcefully
+     * stopping the isolated mode by the image when some complex
+     * action was requested
+     */
+    void sigIsolatedModeChanged();
+
 public slots:
     KisCompositeProgressProxy* compositeProgressProxy();
 
diff --git a/krita/image/kis_mask.cc b/krita/image/kis_mask.cc
index d4ba755..c3953a6 100644
--- a/krita/image/kis_mask.cc
+++ b/krita/image/kis_mask.cc
@@ -179,6 +179,16 @@ KisPaintDeviceSP KisMask::paintDevice() const
     return selection()->getOrCreatePixelSelection();
 }
 
+KisPaintDeviceSP KisMask::original() const
+{
+    return paintDevice();
+}
+
+KisPaintDeviceSP KisMask::projection() const
+{
+    return paintDevice();
+}
+
 void KisMask::setSelection(KisSelectionSP selection)
 {
     m_d->selection = selection;
diff --git a/krita/image/kis_mask.h b/krita/image/kis_mask.h
index 056ec68..92c2eaf 100644
--- a/krita/image/kis_mask.h
+++ b/krita/image/kis_mask.h
@@ -119,6 +119,16 @@ public:
     KisPaintDeviceSP paintDevice() const;
 
     /**
+     * @return the same as paintDevice()
+     */
+    KisPaintDeviceSP original() const;
+
+    /**
+     * @return the same as paintDevice()
+     */
+    KisPaintDeviceSP projection() const;
+
+    /**
      * Change the selection to the specified selection object. The
      * selection is deep copied.
      */
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
index 00755a3..81e1b78 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -454,14 +454,16 @@ void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex
         QAction* mergeLayerDown = menu.addAction(i18n("&Merge with Layer Below"), this, SLOT(slotMergeLayer()));
         if (!index.sibling(index.row() + 1, 0).isValid()) mergeLayerDown->setEnabled(false);
         menu.addSeparator();
+
+        QMenu *convertToMenu = menu.addMenu(i18n("&Convert"));
+        addActionToMenu(convertToMenu, "convert_to_paint_layer");
+        addActionToMenu(convertToMenu, "convert_to_transparency_mask");
+        addActionToMenu(convertToMenu, "convert_to_filter_mask");
+        addActionToMenu(convertToMenu, "convert_to_selection_mask");
+
+        addActionToMenu(&menu, "isolate_layer");
     }
     menu.addSeparator();
-    QMenu *convertToMenu = menu.addMenu(i18n("&Convert"));
-    addActionToMenu(convertToMenu, "convert_to_paint_layer");
-    addActionToMenu(convertToMenu, "convert_to_transparency_mask");
-    addActionToMenu(convertToMenu, "convert_to_filter_mask");
-    addActionToMenu(convertToMenu, "convert_to_selection_mask");
-    menu.addSeparator();
     addActionToMenu(&menu, "add_new_transparency_mask");
     addActionToMenu(&menu, "add_new_filter_mask");
     addActionToMenu(&menu, "add_new_selection_mask");
diff --git a/krita/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp b/krita/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
index ac1601c..0d77ccd 100644
--- a/krita/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
+++ b/krita/plugins/paintops/libpaintop/kis_custom_brush_widget.cpp
@@ -189,7 +189,7 @@ void KisCustomBrushWidget::createBrush()
         KisSelectionSP selection = m_image->globalSelection();
         // create copy of the data
         m_image->lock();
-        KisPaintDeviceSP dev = new KisPaintDevice(*m_image->mergedImage());
+        KisPaintDeviceSP dev = new KisPaintDevice(*m_image->projection());
         m_image->unlock();
 
         if (!selection){
diff --git a/krita/ui/kis_node_manager.cpp b/krita/ui/kis_node_manager.cpp
index 1a7b650..1604ea5 100644
--- a/krita/ui/kis_node_manager.cpp
+++ b/krita/ui/kis_node_manager.cpp
@@ -270,6 +270,16 @@ void KisNodeManager::setup(KActionCollection * actionCollection, KisActionManage
     connect(&m_d->nodeConversionSignalMapper, SIGNAL(mapped(const QString &)),
             this, SLOT(convertNode(const QString &)));
 
+    action = new KisAction(koIcon("view-filter"), i18n("&Isolate Layer"), this);
+    action->setCheckable(true);
+    action->setActivationFlags(KisAction::ACTIVE_NODE);
+    actionManager->addAction("isolate_layer", action, actionCollection);
+    connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool)));
+
+    connect(m_d->view->image(), SIGNAL(sigIsolatedModeChanged()),
+            this, SLOT(slotUpdateIsolateModeAction()));
+    connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotUpdateIsolateModeAction()));
+    connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), SLOT(slotTryFinishIsolatedMode()));
 }
 
 void KisNodeManager::updateGUI()
@@ -333,6 +343,52 @@ void KisNodeManager::moveNodeDirect(KisNodeSP node, KisNodeSP parent, KisNodeSP
     m_d->commandsAdapter->moveNode(node, parent, aboveThis);
 }
 
+void KisNodeManager::toggleIsolateMode(bool checked)
+{
+    KisImageWSP image = m_d->view->image();
+
+    if (checked) {
+        KisNodeSP activeNode = this->activeNode();
+        Q_ASSERT(activeNode);
+
+        image->startIsolatedMode(activeNode);
+    } else {
+        image->stopIsolatedMode();
+    }
+}
+
+void KisNodeManager::slotUpdateIsolateModeAction()
+{
+    KisAction *action = m_d->view->actionManager()->actionByName("isolate_layer");
+    Q_ASSERT(action);
+
+    KisNodeSP activeNode = this->activeNode();
+    KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
+
+    action->setChecked(isolatedRootNode && isolatedRootNode == activeNode);
+}
+
+void KisNodeManager::slotTryFinishIsolatedMode()
+{
+    KisNodeSP isolatedRootNode = m_d->view->image()->isolatedModeRoot();
+    if (!isolatedRootNode) return;
+
+    bool belongsToIsolatedGroup = false;
+
+    KisNodeSP node = this->activeNode();
+    while(node) {
+        if (node == isolatedRootNode) {
+            belongsToIsolatedGroup = true;
+            break;
+        }
+        node = node->parent();
+    }
+
+    if (!belongsToIsolatedGroup) {
+        m_d->view->image()->stopIsolatedMode();
+    }
+}
+
 void KisNodeManager::createNode(const QString & nodeType)
 {
     KisNodeSP activeNode = this->activeNode();
diff --git a/krita/ui/kis_node_manager.h b/krita/ui/kis_node_manager.h
index 9e8596b..1f81239 100644
--- a/krita/ui/kis_node_manager.h
+++ b/krita/ui/kis_node_manager.h
@@ -138,6 +138,10 @@ public slots:
      */
     void moveNodeDirect(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis);
 
+    void toggleIsolateMode(bool checked);
+    void slotUpdateIsolateModeAction();
+    void slotTryFinishIsolatedMode();
+
     void moveNodeAt(KisNodeSP node, KisNodeSP parent, int index);
     void createNode(const QString &node);
     void convertNode(const QString &nodeType);
diff --git a/krita/ui/kis_node_model.cpp b/krita/ui/kis_node_model.cpp
index 8d64888..1b354c3 100644
--- a/krita/ui/kis_node_model.cpp
+++ b/krita/ui/kis_node_model.cpp
@@ -99,6 +99,26 @@ QModelIndex KisNodeModel::indexFromNode(KisNodeSP node) const
     return m_d->indexConverter->indexFromDummy(dummy);
 }
 
+bool KisNodeModel::belongsToIsolatedGroup(KisNodeSP node) const
+{
+    KisNodeSP isolatedRoot = m_d->image->isolatedModeRoot();
+    if (!isolatedRoot) return true;
+
+    KisNodeDummy *isolatedRootDummy =
+        m_d->dummiesFacade->dummyForNode(isolatedRoot);
+    KisNodeDummy *dummy =
+        m_d->dummiesFacade->dummyForNode(node);
+
+    while (dummy) {
+        if (dummy == isolatedRootDummy) {
+            return true;
+        }
+        dummy = dummy->parent();
+    }
+
+    return false;
+}
+
 void KisNodeModel::resetIndexConverter()
 {
     delete m_d->indexConverter;
@@ -116,6 +136,23 @@ void KisNodeModel::resetIndexConverter()
     }
 }
 
+void KisNodeModel::regenerateItems(KisNodeDummy *dummy)
+{
+    const QModelIndex &index = m_d->indexConverter->indexFromDummy(dummy);
+    emit dataChanged(index, index);
+
+    dummy = dummy->firstChild();
+    while (dummy) {
+        regenerateItems(dummy);
+        dummy = dummy->nextSibling();
+    }
+}
+
+void KisNodeModel::slotIsolatedModeChanged()
+{
+    regenerateItems(m_d->dummiesFacade->rootDummy());
+}
+
 void KisNodeModel::updateSettings()
 {
     KisConfig cfg;
@@ -163,14 +200,16 @@ void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect)
 
 void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController)
 {
-    m_d->image = image;
+
     m_d->shapeController = shapeController;
 
     if(m_d->dummiesFacade) {
+        m_d->image->disconnect(this);
         m_d->dummiesFacade->disconnect(this);
         connectDummies(m_d->dummiesFacade->rootDummy(), false);
     }
 
+    m_d->image = image;
     m_d->dummiesFacade = dummiesFacade;
     resetIndexConverter();
 
@@ -191,6 +230,8 @@ void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImag
 
         connect(m_d->dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)),
                 SLOT(slotDummyChanged(KisNodeDummy*)));
+
+        connect(m_d->image, SIGNAL(sigIsolatedModeChanged()), SLOT(slotIsolatedModeChanged()));
     }
 
     reset();
@@ -320,6 +361,8 @@ QVariant KisNodeModel::data(const QModelIndex &index, int role) const
     case Qt::DecorationRole: return node->icon();
     case Qt::EditRole: return node->name();
     case Qt::SizeHintRole: return m_d->image->size(); // FIXME
+    case Qt::TextColorRole:
+        return belongsToIsolatedGroup(node) ? QVariant() : Qt::gray;
     case PropertiesRole: return QVariant::fromValue(node->sectionModelProperties());
     case AspectRatioRole: return double(m_d->image->width()) / m_d->image->height();
     case ProgressRole: {
@@ -327,7 +370,7 @@ QVariant KisNodeModel::data(const QModelIndex &index, int role) const
         return proxy ? proxy->percentage() : -1;
     }
     default:
-        if (role >= int(BeginThumbnailRole))
+        if (role >= int(BeginThumbnailRole) && belongsToIsolatedGroup(node))
             return node->createThumbnail(role - int(BeginThumbnailRole), role - int(BeginThumbnailRole));
         else
             return QVariant();
diff --git a/krita/ui/kis_node_model.h b/krita/ui/kis_node_model.h
index 6352853..76d04eb 100644
--- a/krita/ui/kis_node_model.h
+++ b/krita/ui/kis_node_model.h
@@ -79,6 +79,8 @@ private slots:
     void slotEndRemoveDummy();
     void slotDummyChanged(KisNodeDummy *dummy);
 
+    void slotIsolatedModeChanged();
+
     void updateSettings();
     void processUpdateQueue();
     void progressPercentageChanged(int, const KisNodeSP);
@@ -95,6 +97,10 @@ private:
     bool correctNewNodeLocation(KisNodeSP node,
                                 KisNodeDummy* &parentDummy,
                                 KisNodeDummy* &aboveThisDummy);
+
+    void regenerateItems(KisNodeDummy *dummy);
+    bool belongsToIsolatedGroup(KisNodeSP node) const;
+
 private:
 
     struct Private;
diff --git a/libs/main/KoDocumentSectionDelegate.cpp b/libs/main/KoDocumentSectionDelegate.cpp
index 13d6100..81ae7b1 100644
--- a/libs/main/KoDocumentSectionDelegate.cpp
+++ b/libs/main/KoDocumentSectionDelegate.cpp
@@ -264,9 +264,9 @@ bool KoDocumentSectionDelegate::eventFilter(QObject *object, QEvent *event)
 // PRIVATE
 
 
-QStyleOptionViewItem KoDocumentSectionDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index)
+QStyleOptionViewItemV4 KoDocumentSectionDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index)
 {
-    QStyleOptionViewItem option = o;
+    QStyleOptionViewItemV4 option = o;
     QVariant v = index.data(Qt::FontRole);
     if (v.isValid()) {
         option.font = v.value<QFont>();
diff --git a/libs/main/KoDocumentSectionDelegate.h b/libs/main/KoDocumentSectionDelegate.h
index e804f5d..ad4b9f1 100644
--- a/libs/main/KoDocumentSectionDelegate.h
+++ b/libs/main/KoDocumentSectionDelegate.h
@@ -57,7 +57,7 @@ private:
     class Private;
     Private* const d;
 
-    static QStyleOptionViewItem getOptions(const QStyleOptionViewItem &option, const QModelIndex &index);
+    static QStyleOptionViewItemV4 getOptions(const QStyleOptionViewItem &option, const QModelIndex &index);
     int thumbnailHeight(const QStyleOptionViewItem &option, const QModelIndex &index) const;
     int availableWidth() const;
     int textBoxHeight(const QStyleOptionViewItem &option) const;
_______________________________________________
Krita mailing list
kimageshop at kde.org
https://mail.kde.org/mailman/listinfo/kimageshop
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/kimageshop/attachments/20130523/4ff8847c/attachment-0001.html>


More information about the kimageshop mailing list