[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