[krita/krita-layers-gui-kazakov] /: [FEATURE] Implement filtering of layers basing on their colors

Dmitry Kazakov dimula73 at gmail.com
Wed Feb 10 13:39:41 UTC 2016


Git commit 9ad99e5af5e2de0276a531a7966f21512f8f7656 by Dmitry Kazakov.
Committed on 10/02/2016 at 12:52.
Pushed by dkazakov into branch 'krita-layers-gui-kazakov'.

[FEATURE] Implement filtering of layers basing on their colors

We now have a special proxy model that filters all the layers
which have forbidden colors and that are not active at the moment.

CC:kimageshop at kde.org
Ref T1119,T106

M  +2    -0    libs/ui/CMakeLists.txt
A  +130  -0    libs/ui/kis_node_filter_proxy_model.cpp     [License: GPL (v2+)]
A  +58   -0    libs/ui/kis_node_filter_proxy_model.h     [License: GPL (v2+)]
M  +0    -1    libs/ui/kis_node_model.h
M  +2    -0    libs/ui/kis_node_view_visibility_delegate.cpp
M  +14   -0    libs/ui/tests/kis_node_view_test.cpp
A  +340  -0    libs/ui/widgets/kis_color_filter_combo.cpp     [License: GPL (v2+)]
C  +29   -19   libs/ui/widgets/kis_color_filter_combo.h [from: libs/ui/kis_node_view_visibility_delegate.cpp - 052% similarity]
M  +41   -39   plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M  +5    -1    plugins/extensions/dockers/defaultdockers/kis_layer_box.h
M  +6    -26   plugins/extensions/dockers/defaultdockers/wdglayerbox.ui

http://commits.kde.org/krita/9ad99e5af5e2de0276a531a7966f21512f8f7656

diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 56ca78e..2bc5495 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -126,6 +126,7 @@ set(kritaui_LIB_SRCS
     kis_node_selection_adapter.cpp
     kis_node_insertion_adapter.cpp
     kis_node_model.cpp
+    kis_node_filter_proxy_model.cpp
     kis_model_index_converter_base.cpp
     kis_model_index_converter.cpp
     kis_model_index_converter_show_all.cpp
@@ -255,6 +256,7 @@ set(kritaui_LIB_SRCS
     widgets/kis_floating_message.cpp
     widgets/kis_lod_availability_widget.cpp
     widgets/kis_color_label_selector_widget.cpp
+    widgets/kis_color_filter_combo.cpp
     input/kis_input_manager.cpp
     input/kis_input_manager_p.cpp
     input/kis_extended_modifiers_mapper.cpp
diff --git a/libs/ui/kis_node_filter_proxy_model.cpp b/libs/ui/kis_node_filter_proxy_model.cpp
new file mode 100644
index 0000000..b400ed6
--- /dev/null
+++ b/libs/ui/kis_node_filter_proxy_model.cpp
@@ -0,0 +1,130 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_node_filter_proxy_model.h"
+
+#include <QSet>
+#include "kis_node.h"
+#include "kis_node_model.h"
+#include "kis_node_manager.h"
+#include "kis_signal_compressor.h"
+
+
+struct KisNodeFilterProxyModel::Private
+{
+    Private() : nodeModel(0), activeNodeCompressor(1000, KisSignalCompressor::FIRST_INACTIVE) {}
+
+    KisNodeModel *nodeModel;
+    KisNodeSP pendingActiveNode;
+    KisNodeSP activeNode;
+    QSet<int> acceptedLabels;
+    KisSignalCompressor activeNodeCompressor;
+
+    bool checkIndexAllowedRecursively(QModelIndex srcIndex);
+};
+
+KisNodeFilterProxyModel::KisNodeFilterProxyModel(QObject *parent)
+    : QSortFilterProxyModel(parent),
+      m_d(new Private)
+{
+    connect(&m_d->activeNodeCompressor, SIGNAL(timeout()), SLOT(slotUpdateCurrentNodeFilter()));
+}
+
+KisNodeFilterProxyModel::~KisNodeFilterProxyModel()
+{
+}
+
+void KisNodeFilterProxyModel::setNodeModel(KisNodeModel *model)
+{
+    m_d->nodeModel = model;
+    setSourceModel(model);
+}
+
+bool KisNodeFilterProxyModel::Private::checkIndexAllowedRecursively(QModelIndex srcIndex)
+{
+    KisNodeSP node = nodeModel->nodeFromIndex(srcIndex);
+    if (node == activeNode ||
+        acceptedLabels.contains(node->colorLabelIndex())) {
+
+        return true;
+    }
+
+    bool result = false;
+
+    const int numChildren = srcIndex.model()->rowCount(srcIndex);
+    for (int i = 0; i < numChildren; i++) {
+        QModelIndex child = srcIndex.child(i, 0);
+        if (checkIndexAllowedRecursively(child)) {
+            result = true;
+            break;
+        }
+    }
+
+    return result;
+}
+
+bool KisNodeFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+    KIS_ASSERT_RECOVER(m_d->nodeModel) { return true; }
+
+    const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
+    KisNodeSP node = m_d->nodeModel->nodeFromIndex(index);
+
+    return !node ||
+        m_d->acceptedLabels.isEmpty() ||
+        m_d->checkIndexAllowedRecursively(index);
+}
+
+KisNodeSP KisNodeFilterProxyModel::nodeFromIndex(const QModelIndex &index) const
+{
+    KIS_ASSERT_RECOVER(m_d->nodeModel) { return 0; }
+
+    QModelIndex srcIndex = mapToSource(index);
+    return m_d->nodeModel->nodeFromIndex(srcIndex);
+}
+
+QModelIndex KisNodeFilterProxyModel::indexFromNode(KisNodeSP node) const
+{
+    KIS_ASSERT_RECOVER(m_d->nodeModel) { return QModelIndex(); }
+
+    QModelIndex srcIndex = m_d->nodeModel->indexFromNode(node);
+    return mapFromSource(srcIndex);
+}
+
+void KisNodeFilterProxyModel::setAcceptedLabels(const QList<int> &value)
+{
+    m_d->acceptedLabels = QSet<int>::fromList(value);
+    invalidateFilter();
+}
+
+void KisNodeFilterProxyModel::setActiveNode(KisNodeSP node)
+{
+    m_d->pendingActiveNode = node;
+
+    if (indexFromNode(node).isValid()) {
+        m_d->activeNodeCompressor.start();
+    } else {
+        slotUpdateCurrentNodeFilter();
+    }
+}
+
+void KisNodeFilterProxyModel::slotUpdateCurrentNodeFilter()
+{
+    m_d->activeNode = m_d->pendingActiveNode;
+    invalidateFilter();
+}
diff --git a/libs/ui/kis_node_filter_proxy_model.h b/libs/ui/kis_node_filter_proxy_model.h
new file mode 100644
index 0000000..c32d918
--- /dev/null
+++ b/libs/ui/kis_node_filter_proxy_model.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_NODE_FILTER_PROXY_MODEL_H
+#define __KIS_NODE_FILTER_PROXY_MODEL_H
+
+#include <QScopedPointer>
+#include <QSortFilterProxyModel>
+#include "kis_types.h"
+#include "kritaui_export.h"
+
+class KisNodeModel;
+class KisNodeDummy;
+class KisNodeManager;
+
+
+class KRITAUI_EXPORT KisNodeFilterProxyModel : public QSortFilterProxyModel
+{
+    Q_OBJECT
+public:
+    KisNodeFilterProxyModel(QObject *parent);
+    ~KisNodeFilterProxyModel();
+
+    void setNodeModel(KisNodeModel *model);
+
+    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+    void setAcceptedLabels(const QList<int> &value);
+
+    KisNodeSP nodeFromIndex(const QModelIndex &index) const;
+    QModelIndex indexFromNode(KisNodeSP node) const;
+
+public Q_SLOTS:
+    void setActiveNode(KisNodeSP node);
+
+private Q_SLOTS:
+    void slotUpdateCurrentNodeFilter();
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_NODE_FILTER_PROXY_MODEL_H */
diff --git a/libs/ui/kis_node_model.h b/libs/ui/kis_node_model.h
index 1f52a63..ed6f759 100644
--- a/libs/ui/kis_node_model.h
+++ b/libs/ui/kis_node_model.h
@@ -127,7 +127,6 @@ public:
 
 Q_SIGNALS:
 
-    void nodeActivated(KisNodeSP);
     void toggleIsolateActiveNode();
 
 protected Q_SLOTS:
diff --git a/libs/ui/kis_node_view_visibility_delegate.cpp b/libs/ui/kis_node_view_visibility_delegate.cpp
index e3c9902..f0e80e9 100644
--- a/libs/ui/kis_node_view_visibility_delegate.cpp
+++ b/libs/ui/kis_node_view_visibility_delegate.cpp
@@ -39,6 +39,8 @@ void KisNodeViewVisibilityDelegate::paint(QPainter *painter, const QStyleOptionV
 
 QSize KisNodeViewVisibilityDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
 {
+    Q_UNUSED(index);
+
     KisNodeViewColorScheme scm;
     return QSize(option.rect.width(), scm.rowHeight());
 }
diff --git a/libs/ui/tests/kis_node_view_test.cpp b/libs/ui/tests/kis_node_view_test.cpp
index 43aa76f..0074c20 100644
--- a/libs/ui/tests/kis_node_view_test.cpp
+++ b/libs/ui/tests/kis_node_view_test.cpp
@@ -22,6 +22,7 @@
 #include <QDialog>
 #include <QVBoxLayout>
 #include <QGridLayout>
+#include <QLabel>
 
 #include <KisNodeView.h>
 
@@ -31,6 +32,7 @@
 #include "flake/kis_shape_controller.h"
 #include "kis_undo_adapter.h"
 #include "kis_node_model.h"
+#include "kis_color_filter_combo.h"
 
 
 void KisNodeViewTest::init()
@@ -73,6 +75,18 @@ void KisNodeViewTest::testLayers()
     model->setDummiesFacade(m_shapeController, m_image, m_shapeController, 0, 0);
 
     QVBoxLayout *layout = new QVBoxLayout(&dlg);
+    KisColorFilterCombo *cb = new KisColorFilterCombo(&dlg);
+
+    QSet<int> labels;
+    for (int i = 0; i < 6; i++) {
+        labels.insert(i);
+    }
+    cb->updateAvailableLabels(labels);
+
+    QHBoxLayout *hbox = new QHBoxLayout(&dlg);
+    hbox->addStretch(1);
+    hbox->addWidget(cb);
+    layout->addLayout(hbox);
     layout->addWidget(view);
 
     dlg.resize(280, 400);
diff --git a/libs/ui/widgets/kis_color_filter_combo.cpp b/libs/ui/widgets/kis_color_filter_combo.cpp
new file mode 100644
index 0000000..d877500
--- /dev/null
+++ b/libs/ui/widgets/kis_color_filter_combo.cpp
@@ -0,0 +1,340 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_color_filter_combo.h"
+
+#include <klocalizedstring.h>
+#include <QStylePainter>
+#include <QtCore/qmath.h>
+#include <QApplication>
+#include <QStyleOption>
+#include <QSortFilterProxyModel>
+#include <QStandardItemModel>
+#include <QListView>
+#include <QMouseEvent>
+
+
+
+
+#include "kis_node_view_color_scheme.h"
+#include "kis_debug.h"
+#include "kis_icon_utils.h"
+#include "krita_utils.h"
+#include "kis_node.h"
+
+enum AdditionalRoles {
+    OriginalLabelIndex = Qt::UserRole + 1000
+};
+
+
+struct LabelFilteringModel : public QSortFilterProxyModel
+{
+    LabelFilteringModel(QObject *parent) : QSortFilterProxyModel(parent) {}
+
+    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
+        const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
+        const int labelIndex = index.data(OriginalLabelIndex).toInt();
+
+
+        return labelIndex < 0 || m_acceptedLabels.contains(labelIndex);
+    }
+
+    void setAcceptedLabels(const QSet<int> &value) {
+        m_acceptedLabels = value;
+        invalidateFilter();
+    }
+
+private:
+    QSet<int> m_acceptedLabels;
+};
+
+
+struct ComboEventFilter : public QObject {
+    ComboEventFilter(KisColorFilterCombo *parent) : m_parent(parent), m_buttonPressed(false) {}
+
+    bool eventFilter(QObject *obj, QEvent *event) {
+        if (event->type() == QEvent::Leave) {
+            m_buttonPressed = false;
+
+        } else if (event->type() == QEvent::MouseButtonPress) {
+            QMouseEvent *mevent = static_cast<QMouseEvent*>(event);
+            m_buttonPressed = mevent->button() == Qt::LeftButton;
+
+        } else if (event->type() == QEvent::MouseButtonRelease) {
+            QMouseEvent *mevent = static_cast<QMouseEvent*>(event);
+            QModelIndex index = m_parent->view()->indexAt(mevent->pos());
+            if (!index.isValid()) return false;
+
+            /**
+             * We should eat the first event that arrives exactly when
+             * the drop down appears on screen.
+             */
+            if (!m_buttonPressed) return true;
+
+            const bool toUncheckedState = index.data(Qt::CheckStateRole) == Qt::Checked;
+
+            if (toUncheckedState) {
+                m_parent->model()->setData(index, Qt::Unchecked, Qt::CheckStateRole);
+            } else {
+                m_parent->model()->setData(index, Qt::Checked, Qt::CheckStateRole);
+            }
+
+            if (index.data(OriginalLabelIndex).toInt() == -1) {
+                for (int i = 0; i < m_parent->model()->rowCount(); i++) {
+                    const QModelIndex &other = m_parent->model()->index(i, 0);
+                    if (other.data(OriginalLabelIndex) != -1) {
+                        m_parent->model()->setData(other, toUncheckedState ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
+                    }
+                }
+            } else {
+                bool prevChecked = false;
+                bool checkedVaries = false;
+                QModelIndex allLabelsIndex;
+
+                for (int i = 0; i < m_parent->model()->rowCount(); i++) {
+                    const QModelIndex &other = m_parent->model()->index(i, 0);
+                    if (other.data(OriginalLabelIndex) != -1) {
+                        const bool currentChecked = other.data(Qt::CheckStateRole) == Qt::Checked;
+
+                        if (i == 0) {
+                            prevChecked = currentChecked;
+                        } else {
+                            if (prevChecked != currentChecked) {
+                                checkedVaries = true;
+                                break;
+                            }
+                        }
+                    } else {
+                        allLabelsIndex = other;
+                    }
+                }
+
+                const bool allLabelsIndexShouldBeChecked =
+                    prevChecked && !checkedVaries;
+
+                if (allLabelsIndexShouldBeChecked !=
+                    (allLabelsIndex.data(Qt::CheckStateRole) == Qt::Checked)) {
+
+                    m_parent->model()->setData(allLabelsIndex, allLabelsIndexShouldBeChecked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole);
+                }
+            }
+
+            emit m_parent->selectedColorsChanged();
+
+            m_buttonPressed = false;
+            return true;
+        }
+
+        return QObject::eventFilter(obj, event);
+    }
+
+    KisColorFilterCombo *m_parent;
+    bool m_buttonPressed;
+};
+
+class FullSizedListView : public QListView
+{
+public:
+    QSize sizeHint() const {
+        return contentsSize();
+    }
+};
+
+struct KisColorFilterCombo::Private
+{
+    LabelFilteringModel *filteringModel;
+};
+
+KisColorFilterCombo::KisColorFilterCombo(QWidget *parent)
+    : QComboBox(parent),
+      m_d(new Private)
+{
+    QStandardItemModel *newModel = new QStandardItemModel(this);
+    setModel(newModel);
+
+    setView(new FullSizedListView);
+    view()->installEventFilter(new ComboEventFilter(this));
+    view()->viewport()->installEventFilter(new ComboEventFilter(this));
+
+    KisNodeViewColorScheme scm;
+
+    QStandardItem* item = new QStandardItem(i18nc("combo box: show all layers", "All"));
+    item->setCheckable(true);
+    item->setCheckState(Qt::Unchecked);
+    item->setData(QColor(Qt::transparent), Qt::BackgroundColorRole);
+    item->setData(int(-1), OriginalLabelIndex);
+    item->setData(QSize(30, scm.rowHeight()), Qt::SizeHintRole);
+    newModel->appendRow(item);
+
+    int labelIndex = 0;
+    foreach (const QColor &color, scm.allColorLabels()) {
+        const QString title = color.alpha() > 0 ? "" : i18nc("combo box: select all layers without a label", "No Label");
+
+        QStandardItem* item = new QStandardItem(title);
+        item->setCheckable(true);
+        item->setCheckState(Qt::Unchecked);
+        item->setData(color, Qt::BackgroundColorRole);
+        item->setData(labelIndex, OriginalLabelIndex);
+        item->setData(QSize(30, scm.rowHeight()), Qt::SizeHintRole);
+        newModel->appendRow(item);
+
+        labelIndex++;
+    }
+
+    m_d->filteringModel = new LabelFilteringModel(this);
+    QAbstractItemModel *originalModel = model();
+    originalModel->setParent(m_d->filteringModel);
+
+    m_d->filteringModel->setSourceModel(originalModel);
+    setModel(m_d->filteringModel);
+}
+
+KisColorFilterCombo::~KisColorFilterCombo()
+{
+}
+
+void collectAvailableLabels(KisNodeSP root, QSet<int> *labels)
+{
+    labels->insert(root->colorLabelIndex());
+
+    KisNodeSP node = root->firstChild();
+    while (node) {
+        collectAvailableLabels(node, labels);
+        node = node->nextSibling();
+    }
+}
+
+void KisColorFilterCombo::updateAvailableLabels(KisNodeSP rootNode)
+{
+    QSet<int> labels;
+    collectAvailableLabels(rootNode, &labels);
+
+    updateAvailableLabels(labels);
+}
+
+void KisColorFilterCombo::updateAvailableLabels(const QSet<int> &labels)
+{
+    m_d->filteringModel->setAcceptedLabels(labels);
+}
+
+QList<int> KisColorFilterCombo::selectedColors() const
+{
+    QList<int> colors;
+    for (int i = 0; i < model()->rowCount(); i++) {
+        const QModelIndex &other = model()->index(i, 0);
+        const int label = other.data(OriginalLabelIndex).toInt();
+
+        if (label != -1 &&
+            other.data(Qt::CheckStateRole) == Qt::Checked) {
+
+            colors << label;
+        }
+    }
+    return colors;
+}
+
+void KisColorFilterCombo::paintEvent(QPaintEvent *event)
+{
+    Q_UNUSED(event);
+
+    QStylePainter painter(this);
+    painter.setPen(palette().color(QPalette::Text));
+
+    // draw the combobox frame, focusrect and selected etc.
+    QStyleOptionComboBox opt;
+    initStyleOption(&opt);
+    painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+
+    {
+        KisNodeViewColorScheme scm;
+        const QRect editRect = style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this);
+        const int size = qMin(editRect.width(), editRect.height());
+
+        const QList<int> selectedColors = this->selectedColors();
+
+        if (selectedColors.size() == 0 || selectedColors.size() == model()->rowCount() - 1) {
+            QIcon icon = KisIconUtils::loadIcon("view-filter");
+            QPixmap pixmap = icon.pixmap(QSize(size, size), !isEnabled() ? QIcon::Disabled : QIcon::Normal);
+            painter.drawPixmap(editRect.right() - size, editRect.top(), pixmap);
+
+        } else if (selectedColors.size() == 1) {
+            const int currentLabel = selectedColors.first();
+            QColor currentColor = scm.colorLabel(currentLabel);
+
+            if (currentColor.alpha() > 0) {
+                painter.fillRect(editRect, currentColor);
+            } else if (currentLabel == 0) {
+                QPen oldPen = painter.pen();
+
+                const int border = 4;
+                QRect crossRect(0, 0, size - 2 * border, size - 2 * border);
+                crossRect.moveCenter(editRect.center());
+
+                QColor shade = opt.palette.dark().color();
+                painter.setPen(QPen(shade, 2));
+                painter.drawLine(crossRect.topLeft(), crossRect.bottomRight());
+                painter.drawLine(crossRect.bottomLeft(), crossRect.topRight());
+            }
+        } else {
+            const int border = 0;
+            QRect pieRect(0, 0, size - 2 * border, size - 2 * border);
+            pieRect.moveCenter(editRect.center());
+
+            const int numColors = selectedColors.size();
+            const int step = 16 * 360 / numColors;
+
+            int currentAngle = 0;
+
+            //painter.save(); // optimize out!
+            painter.setRenderHint(QPainter::Antialiasing);
+
+            for (int i = 0; i < numColors; i++) {
+                QColor color = scm.colorLabel(selectedColors[i]);
+                QBrush brush = color.alpha() > 0 ? QBrush(color) : QBrush(Qt::black, Qt::Dense4Pattern);
+                painter.setPen(color);
+                painter.setBrush(brush);
+
+                painter.drawPie(pieRect, currentAngle, step);
+                currentAngle += step;
+            }
+            //painter.restore(); // optimize out!
+        }
+    }
+
+    // draw the icon and text
+    //painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+}
+
+QSize KisColorFilterCombo::minimumSizeHint() const
+{
+    return sizeHint();
+}
+
+QSize KisColorFilterCombo::sizeHint() const
+{
+    QStyleOptionComboBox opt;
+    initStyleOption(&opt);
+
+    const QStyleOption *baseOption = qstyleoption_cast<const QStyleOption *>(&opt);
+    const int arrowSize = style()->pixelMetric(QStyle::PM_ScrollBarExtent, baseOption, this);
+
+    const QSize originalHint = QComboBox::sizeHint();
+    QSize sh(3 * arrowSize, originalHint.height());
+
+    return sh.expandedTo(QApplication::globalStrut());
+}
diff --git a/libs/ui/kis_node_view_visibility_delegate.cpp b/libs/ui/widgets/kis_color_filter_combo.h
similarity index 52%
copy from libs/ui/kis_node_view_visibility_delegate.cpp
copy to libs/ui/widgets/kis_color_filter_combo.h
index e3c9902..ed9ce07 100644
--- a/libs/ui/kis_node_view_visibility_delegate.cpp
+++ b/libs/ui/widgets/kis_color_filter_combo.h
@@ -16,29 +16,39 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#include "kis_node_view_visibility_delegate.h"
+#ifndef __KIS_COLOR_FILTER_COMBO_H
+#define __KIS_COLOR_FILTER_COMBO_H
 
-#include "kis_node_view_color_scheme.h"
+#include <QScopedPointer>
+#include <QComboBox>
+#include "kritaui_export.h"
+#include "kis_types.h"
 
 
-KisNodeViewVisibilityDelegate::KisNodeViewVisibilityDelegate(QObject *parent)
-    : QAbstractItemDelegate(parent)
+class KRITAUI_EXPORT KisColorFilterCombo : public QComboBox
 {
-}
+    Q_OBJECT
+public:
+    KisColorFilterCombo(QWidget *parent);
+    ~KisColorFilterCombo();
 
-KisNodeViewVisibilityDelegate::~KisNodeViewVisibilityDelegate()
-{
-}
+    void updateAvailableLabels(KisNodeSP rootNode);
+    void updateAvailableLabels(const QSet<int> &labels);
 
-void KisNodeViewVisibilityDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
-    Q_UNUSED(painter);
-    Q_UNUSED(option);
-    Q_UNUSED(index);
-}
+    QSize minimumSizeHint() const;
+    QSize sizeHint() const;
 
-QSize KisNodeViewVisibilityDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
-    KisNodeViewColorScheme scm;
-    return QSize(option.rect.width(), scm.rowHeight());
-}
+    QList<int> selectedColors() const;
+
+Q_SIGNALS:
+    void selectedColorsChanged();
+
+private:
+    void paintEvent(QPaintEvent *event);
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_COLOR_FILTER_COMBO_H */
diff --git a/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
index c6a23de..3d9cba2 100644
--- a/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -70,6 +70,7 @@
 #include "KisViewManager.h"
 #include "kis_node_manager.h"
 #include "kis_node_model.h"
+
 #include "canvas/kis_canvas2.h"
 #include "KisDocument.h"
 #include "kis_dummies_facade_base.h"
@@ -81,6 +82,8 @@
 #include "sync_button_and_action.h"
 #include "kis_color_label_selector_widget.h"
 #include "kis_signals_blocker.h"
+#include "kis_color_filter_combo.h"
+#include "kis_node_filter_proxy_model.h"
 
 #include "ui_wdglayerbox.h"
 
@@ -105,6 +108,7 @@ KisLayerBox::KisLayerBox()
         , m_canvas(0)
         , m_wdgLayerBox(new Ui_WdgLayerBox)
         , m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE)
+        , m_colorLabelCompressor(900, KisSignalCompressor::FIRST_INACTIVE)
 {
     KisConfig cfg;
 
@@ -124,29 +128,8 @@ KisLayerBox::KisLayerBox()
     connect(m_wdgLayerBox->listLayers,
             SIGNAL(selectionChanged(const QModelIndexList&)), SLOT(selectionChanged(const QModelIndexList&)));
 
-    m_viewModeMenu = new QMenu(this);
-    QActionGroup *group = new QActionGroup(this);
-    QList<QAction*> actions;
-
-    actions << m_viewModeMenu->addAction(KisIconUtils::loadIcon("view-list-text"),
-                                         i18n("Minimal View"), this, SLOT(slotMinimalView()));
-    actions << m_viewModeMenu->addAction(KisIconUtils::loadIcon("view-list-details"),
-                                         i18n("Detailed View"), this, SLOT(slotDetailedView()));
-    actions << m_viewModeMenu->addAction(KisIconUtils::loadIcon("view-preview"),
-                                         i18n("Thumbnail View"), this, SLOT(slotThumbnailView()));
-
-    for (int i = 0, n = actions.count(); i < n; ++i) {
-        actions[i]->setCheckable(true);
-        actions[i]->setActionGroup(group);
-    }
-
     m_wdgLayerBox->bnAdd->setIcon(KisIconUtils::loadIcon("addlayer"));
 
-    m_wdgLayerBox->bnViewMode->setMenu(m_viewModeMenu);
-    m_wdgLayerBox->bnViewMode->setPopupMode(QToolButton::InstantPopup);
-    m_wdgLayerBox->bnViewMode->setIcon(KisIconUtils::loadIcon("view-choose"));
-    m_wdgLayerBox->bnViewMode->setText(i18n("View mode"));
-
     m_wdgLayerBox->bnDelete->setIcon(KisIconUtils::loadIcon("deletelayer"));
     m_wdgLayerBox->bnDelete->setIconSize(QSize(22, 22));
 
@@ -188,6 +171,8 @@ KisLayerBox::KisLayerBox()
     m_wdgLayerBox->bnAdd->setPopupMode(QToolButton::MenuButtonPopup);
 
     m_nodeModel = new KisNodeModel(this);
+    m_filteringModel = new KisNodeFilterProxyModel(this);
+    m_filteringModel->setNodeModel(m_nodeModel);
 
     /**
      * Connect model updateUI() to enable/disable controls.
@@ -215,11 +200,17 @@ KisLayerBox::KisLayerBox()
     m_colorSelectorAction = new QWidgetAction(this);
     m_colorSelectorAction->setDefaultWidget(m_colorSelector);
 
-    m_wdgLayerBox->listLayers->setModel(m_nodeModel);
+    connect(m_nodeModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+            &m_colorLabelCompressor, SLOT(start()));
+
+    m_wdgLayerBox->listLayers->setModel(m_filteringModel);
+
+    connect(m_wdgLayerBox->cmbFilter, SIGNAL(selectedColorsChanged()), SLOT(updateLayerFiltering()));
 
     setEnabled(false);
 
     connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail()));
+    connect(&m_colorLabelCompressor, SIGNAL(timeout()), SLOT(updateAvailableLabels()));
 }
 
 KisLayerBox::~KisLayerBox()
@@ -228,24 +219,24 @@ KisLayerBox::~KisLayerBox()
 }
 
 
-void expandNodesRecursively(KisNodeSP root, QPointer<KisNodeModel> nodeModel, KisNodeView *nodeView)
+void expandNodesRecursively(KisNodeSP root, QPointer<KisNodeFilterProxyModel> filteringModel, KisNodeView *nodeView)
 {
     if (!root) return;
-    if (nodeModel.isNull()) return;
+    if (filteringModel.isNull()) return;
     if (!nodeView) return;
 
     nodeView->blockSignals(true);
 
     KisNodeSP node = root->firstChild();
     while (node) {
-        QModelIndex idx = nodeModel->indexFromNode(node);
+        QModelIndex idx = filteringModel->indexFromNode(node);
         if (idx.isValid()) {
             if (node->collapsed()) {
                 nodeView->collapse(idx);
             }
         }
         if (node->childCount() > 0) {
-            expandNodesRecursively(node, nodeModel, nodeView);
+            expandNodesRecursively(node, filteringModel, nodeView);
         }
         node = node->nextSibling();
     }
@@ -301,7 +292,6 @@ void KisLayerBox::setCanvas(KoCanvasBase *canvas)
         disconnect(m_image, 0, this, 0);
         disconnect(m_nodeManager, 0, this, 0);
         disconnect(m_nodeModel, 0, m_nodeManager, 0);
-        disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI()));
         m_nodeManager->slotSetSelectedNodes(KisNodeList());
     }
 
@@ -343,8 +333,9 @@ void KisLayerBox::setCanvas(KoCanvasBase *canvas)
                 m_nodeManager, SLOT(toggleIsolateActiveNode()));
 
         m_wdgLayerBox->listLayers->expandAll();
-        expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers);
+        expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
         m_wdgLayerBox->listLayers->scrollTo(m_wdgLayerBox->listLayers->currentIndex());
+        updateAvailableLabels();
 
         addActionToMenu(m_newLayerMenu, "add_new_paint_layer");
         addActionToMenu(m_newLayerMenu, "add_new_group_layer");
@@ -374,7 +365,6 @@ void KisLayerBox::unsetCanvas()
     disconnect(m_image, 0, this, 0);
     disconnect(m_nodeManager, 0, this, 0);
     disconnect(m_nodeModel, 0, m_nodeManager, 0);
-    disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI()));
     m_nodeManager->slotSetSelectedNodes(KisNodeList());
 
     m_canvas = 0;
@@ -439,9 +429,10 @@ void KisLayerBox::updateUI()
  */
 void KisLayerBox::setCurrentNode(KisNodeSP node)
 {
-    QModelIndex index = node ? m_nodeModel->indexFromNode(node) : QModelIndex();
+    m_filteringModel->setActiveNode(node);
 
-    m_nodeModel->setData(index, true, KisNodeModel::ActiveRole);
+    QModelIndex index = node ? m_filteringModel->indexFromNode(node) : QModelIndex();
+    m_filteringModel->setData(index, true, KisNodeModel::ActiveRole);
     updateUI();
 }
 
@@ -450,7 +441,7 @@ void KisLayerBox::slotModelReset()
     if(m_nodeModel->hasDummiesFacade()) {
         QItemSelection selection;
         Q_FOREACH (const KisNodeSP node, m_nodeManager->selectedNodes()) {
-            const QModelIndex &idx = m_nodeModel->indexFromNode(node);
+            const QModelIndex &idx = m_filteringModel->indexFromNode(node);
             if(idx.isValid()){
                 QItemSelectionRange selectionRange(idx);
                 selection << selectionRange;
@@ -552,7 +543,7 @@ void KisLayerBox::slotContextMenuRequested(const QPoint &pos, const QModelIndex
                 addActionToMenu(splitAlphaMenu, "split_alpha_write");
                 addActionToMenu(splitAlphaMenu, "split_alpha_save_merged");
 
-                KisNodeSP node = m_nodeModel->nodeFromIndex(index);
+                KisNodeSP node = m_filteringModel->nodeFromIndex(index);
                 if (node && !node->inherits("KisTransformMask")) {
                     addActionToMenu(&menu, "isolate_layer");
                 }
@@ -643,7 +634,7 @@ void KisLayerBox::slotOpacitySliderMoved(qreal opacity)
 
 void KisLayerBox::slotCollapsed(const QModelIndex &index)
 {
-    KisNodeSP node = m_nodeModel->nodeFromIndex(index);
+    KisNodeSP node = m_filteringModel->nodeFromIndex(index);
     if (node) {
         node->setCollapsed(true);
     }
@@ -651,7 +642,7 @@ void KisLayerBox::slotCollapsed(const QModelIndex &index)
 
 void KisLayerBox::slotExpanded(const QModelIndex &index)
 {
-    KisNodeSP node = m_nodeModel->nodeFromIndex(index);
+    KisNodeSP node = m_filteringModel->nodeFromIndex(index);
     if (node) {
         node->setCollapsed(false);
     }
@@ -669,7 +660,7 @@ void KisLayerBox::slotSelectOpaque()
 void KisLayerBox::slotNodeCollapsedChanged()
 {
     m_wdgLayerBox->listLayers->expandAll();
-    expandNodesRecursively(m_image->rootLayer(), m_nodeModel, m_wdgLayerBox->listLayers);
+    expandNodesRecursively(m_image->rootLayer(), m_filteringModel, m_wdgLayerBox->listLayers);
 }
 
 inline bool isSelectionMask(KisNodeSP node)
@@ -748,7 +739,7 @@ void KisLayerBox::selectionChanged(const QModelIndexList selection)
      */
     if (selection.isEmpty() && m_nodeManager->activeNode()) {
         QModelIndex selectedIndex =
-            m_nodeModel->indexFromNode(m_nodeManager->activeNode());
+            m_filteringModel->indexFromNode(m_nodeManager->activeNode());
 
         m_wdgLayerBox->listLayers->selectionModel()->
             setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect);
@@ -757,7 +748,7 @@ void KisLayerBox::selectionChanged(const QModelIndexList selection)
 
     QList<KisNodeSP> selectedNodes;
     Q_FOREACH (const QModelIndex &idx, selection) {
-        selectedNodes << m_nodeModel->nodeFromIndex(idx);
+        selectedNodes << m_filteringModel->nodeFromIndex(idx);
     }
 
     m_nodeManager->slotSetSelectedNodes(selectedNodes);
@@ -770,7 +761,7 @@ void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes)
 
     QModelIndexList newSelection;
     Q_FOREACH(KisNodeSP node, nodes) {
-        newSelection << m_nodeModel->indexFromNode(node);
+        newSelection << m_filteringModel->indexFromNode(node);
     }
 
     QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel();
@@ -807,4 +798,15 @@ void KisLayerBox::slotColorLabelChanged(int index)
     }
 }
 
+void KisLayerBox::updateAvailableLabels()
+{
+    if (!m_image) return;
+    m_wdgLayerBox->cmbFilter->updateAvailableLabels(m_image->root());
+}
+
+void KisLayerBox::updateLayerFiltering()
+{
+    m_filteringModel->setAcceptedLabels(m_wdgLayerBox->cmbFilter->selectedColors());
+}
+
 #include "moc_kis_layer_box.cpp"
diff --git a/plugins/extensions/dockers/defaultdockers/kis_layer_box.h b/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
index f359efc..ab6b317 100644
--- a/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
+++ b/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
@@ -50,6 +50,7 @@ class QAbstractButton;
 class KoCompositeOp;
 class KisCanvas2;
 class KisNodeModel;
+class KisNodeFilterProxyModel;
 class Ui_WdgLayerBox;
 class KisNodeJugglerCompressed;
 class KisColorLabelSelectorWidget;
@@ -120,6 +121,8 @@ private Q_SLOTS:
     void slotColorLabelChanged(int index);
 
     void updateThumbnail();
+    void updateAvailableLabels();
+    void updateLayerFiltering();
 
 private:
     inline void connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id);
@@ -129,10 +132,10 @@ private:
 private:
 
     KisCanvas2* m_canvas;
-    QMenu *m_viewModeMenu;
     QMenu *m_newLayerMenu;
     KisImageWSP m_image;
     QPointer<KisNodeModel> m_nodeModel;
+    QPointer<KisNodeFilterProxyModel> m_filteringModel;
     QPointer<KisNodeManager> m_nodeManager;
     QPointer<KisColorLabelSelectorWidget> m_colorSelector;
     QPointer<QWidgetAction> m_colorSelectorAction;
@@ -145,6 +148,7 @@ private:
     KisAction* m_propertiesAction;
     KisAction* m_selectOpaque;
     KisSignalCompressor m_thumbnailCompressor;
+    KisSignalCompressor m_colorLabelCompressor;
 };
 
 class KisLayerBoxFactory : public KoDockFactoryBase
diff --git a/plugins/extensions/dockers/defaultdockers/wdglayerbox.ui b/plugins/extensions/dockers/defaultdockers/wdglayerbox.ui
index d4b3087..f04b7b4 100644
--- a/plugins/extensions/dockers/defaultdockers/wdglayerbox.ui
+++ b/plugins/extensions/dockers/defaultdockers/wdglayerbox.ui
@@ -57,32 +57,7 @@
       </widget>
      </item>
      <item>
-      <widget class="QToolButton" name="bnViewMode">
-       <property name="minimumSize">
-        <size>
-         <width>22</width>
-         <height>22</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>22</width>
-         <height>22</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>...</string>
-       </property>
-       <property name="iconSize">
-        <size>
-         <width>16</width>
-         <height>16</height>
-        </size>
-       </property>
-       <property name="autoRaise">
-        <bool>true</bool>
-       </property>
-      </widget>
+      <widget class="KisColorFilterCombo" name="cmbFilter"/>
      </item>
     </layout>
    </item>
@@ -329,6 +304,11 @@
    <extends>QToolButton</extends>
    <header>kis_tool_button.h</header>
   </customwidget>
+  <customwidget>
+   <class>KisColorFilterCombo</class>
+   <extends>QComboBox</extends>
+   <header>kis_color_filter_combo.h</header>
+  </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>cmbComposite</tabstop>



More information about the kimageshop mailing list