diff --git a/libs/ui/widgets/kis_color_label_button.cpp b/libs/ui/widgets/kis_color_label_button.cpp index 1e47e781cb..92cf9786fb 100644 --- a/libs/ui/widgets/kis_color_label_button.cpp +++ b/libs/ui/widgets/kis_color_label_button.cpp @@ -1,111 +1,123 @@ #include "kis_color_label_button.h" #include #include #include #include "kis_global.h" #include "kis_debug.h" +#include "krita_container_utils.h" struct KisColorLabelButton::Private { QColor color; }; KisColorLabelButton::KisColorLabelButton(QColor color, QWidget *parent) : QAbstractButton(parent), m_d(new Private()) { setCheckable(true); setChecked(true); m_d->color = color; } KisColorLabelButton::~KisColorLabelButton() { } void KisColorLabelButton::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); QStylePainter painter(this); QStyleOption styleOption; styleOption.initFrom(this); if (isDown() || isChecked()){ styleOption.state |= QStyle::State_On; } // Draw fill.. QRect fillRect = kisGrowRect(rect(), -2); fillRect.width(); if (m_d->color.alpha() > 0) { QColor fillColor = m_d->color; if (!isChecked()) { fillColor.setAlpha(32); } painter.fillRect(fillRect, fillColor); } else { // draw an X for no color for the first item const int shortestEdge = std::min(fillRect.width(), fillRect.height()); const int longestEdge = std::max(fillRect.width(), fillRect.height()); bool horizontalIsShortest = (shortestEdge == fillRect.width()); QRect srcRect = horizontalIsShortest ? fillRect.adjusted(0, (longestEdge / 2) - (shortestEdge / 2), 0, (shortestEdge / 2) - (longestEdge / 2)) : fillRect.adjusted((longestEdge / 2) - (shortestEdge / 2), 0, (shortestEdge / 2) - (longestEdge / 2), 0); QRect crossRect = kisGrowRect(srcRect, -1); QColor shade = styleOption.palette.text().color(); if (!isChecked()) { shade.setAlpha(64); } painter.setPen(QPen(shade, 2)); painter.drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter.drawLine(crossRect.bottomLeft(), crossRect.topRight()); } } QSize KisColorLabelButton::sizeHint() const { return QSize(16,32); } void KisColorLabelButton::nextCheckState() { KisColorLabelButtonGroup* colorLabelButtonGroup = dynamic_cast(group()); - if (colorLabelButtonGroup && (colorLabelButtonGroup->viableButtonsChecked() > 1 || !isChecked())) { + if (colorLabelButtonGroup && (colorLabelButtonGroup->viableCheckedButtonCount() > 1 || !isChecked())) { setChecked(!isChecked()); } else { setChecked(isChecked()); } } KisColorLabelButtonGroup::KisColorLabelButtonGroup(QObject *parent) : QButtonGroup(parent) { connect(this, SIGNAL(buttonToggled(QAbstractButton*,bool)), this, SLOT(slotRegisterButtonState(QAbstractButton*,bool))); } KisColorLabelButtonGroup::~KisColorLabelButtonGroup() { } -int KisColorLabelButtonGroup::viableButtonsChecked() { - int count = 0; +QList KisColorLabelButtonGroup::viableButtons() { + QList viableButtons = this->buttons(); + KritaUtils::filterContainer(viableButtons, [](QAbstractButton* btn){ + return (btn->isVisible()); + }); + return viableButtons; +} - Q_FOREACH( QAbstractButton* btn, buttons()) { - count += (btn->isVisible() && btn->isChecked()) ? 1 : 0; - } +QList KisColorLabelButtonGroup::checkedViableButtons() { + QList checkedButtons = viableButtons(); + KritaUtils::filterContainer(checkedButtons, [](QAbstractButton* btn){ + return (btn->isChecked()); + }); + + return checkedButtons; +} - return count; +int KisColorLabelButtonGroup::viableCheckedButtonCount() { + return checkedViableButtons().count(); } void KisColorLabelButtonGroup::reset() { Q_FOREACH( QAbstractButton* btn, buttons()) { btn->setChecked(true); } } diff --git a/libs/ui/widgets/kis_color_label_button.h b/libs/ui/widgets/kis_color_label_button.h index 6914b4233f..da374c2490 100644 --- a/libs/ui/widgets/kis_color_label_button.h +++ b/libs/ui/widgets/kis_color_label_button.h @@ -1,41 +1,43 @@ #ifndef KISCOLORLABELBUTTON_H #define KISCOLORLABELBUTTON_H #include #include #include "kritaui_export.h" class KRITAUI_EXPORT KisColorLabelButton : public QAbstractButton { Q_OBJECT public: KisColorLabelButton(QColor color, QWidget *parent = nullptr); ~KisColorLabelButton(); void paintEvent(QPaintEvent* event) override; QSize sizeHint() const override; virtual void nextCheckState() override; Q_SIGNALS: void visibilityChanged(QAbstractButton* btn, bool isVisible); private: struct Private; const QScopedPointer m_d; }; class KRITAUI_EXPORT KisColorLabelButtonGroup : public QButtonGroup { Q_OBJECT public: KisColorLabelButtonGroup(QObject* parent); ~KisColorLabelButtonGroup(); - int viableButtonsChecked(); + QList viableButtons(); + QList checkedViableButtons(); + int viableCheckedButtonCount(); void reset(); }; #endif // KISCOLORLABELBUTTON_H diff --git a/libs/ui/widgets/kis_layer_filter_widget.cpp b/libs/ui/widgets/kis_layer_filter_widget.cpp index 95c73b0789..8cb200c14c 100644 --- a/libs/ui/widgets/kis_layer_filter_widget.cpp +++ b/libs/ui/widgets/kis_layer_filter_widget.cpp @@ -1,227 +1,241 @@ #include "kis_layer_filter_widget.h" #include #include #include #include #include #include #include #include "kis_debug.h" #include "kis_node.h" #include "kis_color_label_button.h" #include "kis_color_label_selector_widget.h" #include "kis_node_view_color_scheme.h" KisLayerFilterWidget::KisLayerFilterWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); textFilter = new QLineEdit(this); textFilter->setPlaceholderText("Filter by name..."); textFilter->setMinimumWidth(256); textFilter->setMinimumHeight(32); textFilter->setClearButtonEnabled(true); - connect(textFilter, SIGNAL(textChanged(QString)), this, SIGNAL(filteringOptionsChanged())); KisNodeViewColorScheme colorScheme; QWidget *buttonContainer = new QWidget(this); buttonContainer->setToolTip("Filter by color label..."); buttonEventFilter = new EventFilter(buttonContainer); { QHBoxLayout *subLayout = new QHBoxLayout(buttonContainer); buttonContainer->setLayout(subLayout); subLayout->setContentsMargins(0,0,0,0); buttonGroup = new KisColorLabelButtonGroup(buttonContainer); buttonGroup->setExclusive(false); foreach (const QColor &color, colorScheme.allColorLabels()) { KisColorLabelButton* btn = new KisColorLabelButton(color, buttonContainer); buttonGroup->addButton(btn); btn->setVisible(false); btn->installEventFilter(buttonEventFilter); subLayout->addWidget(btn); colorLabelButtons.append(btn); } connect(buttonGroup, SIGNAL(buttonToggled(int,bool)), this, SIGNAL(filteringOptionsChanged())); } QPushButton *resetButton = new QPushButton("Reset Filters", this); connect(resetButton, &QPushButton::clicked, [this](){ textFilter->clear(); buttonGroup->reset(); }); layout->addWidget(textFilter); layout->addWidget(buttonContainer); layout->addWidget(resetButton); } void KisLayerFilterWidget::scanUsedColorLabels(KisNodeSP node, QSet &colorLabels) { if (node->parent()) { colorLabels.insert(node->colorLabelIndex()); } KisNodeSP child = node->firstChild(); while(child) { scanUsedColorLabels(child, colorLabels); child = child->nextSibling(); } } void KisLayerFilterWidget::updateColorLabels(KisNodeSP root) { QSet colorLabels; scanUsedColorLabels(root, colorLabels); if (colorLabels.size() > 1) { colorLabelButtons[0]->parentWidget()->setVisible(true); - for (size_t index = 0; index < colorLabelButtons.size(); index++) { + for (int index = 0; index < colorLabelButtons.size(); index++) { if (colorLabels.contains(index)) { colorLabelButtons[index]->setVisible(true); } else { colorLabelButtons[index]->setVisible(false); colorLabelButtons[index]->setChecked(true); } } } else { colorLabelButtons[0]->parentWidget()->setVisible(false); } } bool KisLayerFilterWidget::isCurrentlyFiltering() { const bool isFilteringText = !textFilter->text().isEmpty(); bool isFilteringColors = false; for (int index = 0; index < colorLabelButtons.size(); index++) { if (colorLabelButtons[index]->isVisible() && !colorLabelButtons[index]->isChecked()) { isFilteringColors = true; } } return isFilteringText || isFilteringColors; } QList KisLayerFilterWidget::getActiveColors() { QList activeColors; for (int index = 0; index < colorLabelButtons.size(); index++) { if (!colorLabelButtons[index]->isVisible() || colorLabelButtons[index]->isChecked()) { activeColors.append(index); } } return activeColors; } QString KisLayerFilterWidget::getTextFilter() { return textFilter->text(); } void KisLayerFilterWidget::reset() { textFilter->clear(); for (int index = 0; index < colorLabelButtons.size(); index++) { colorLabelButtons[index]->setChecked(true); } filteringOptionsChanged(); } KisLayerFilterWidget::EventFilter::EventFilter(QWidget *buttonContainer, QObject* parent) : QObject(parent) { m_buttonContainer = buttonContainer; lastKnownMousePosition = QPoint(0,0); currentState = Idle; } bool KisLayerFilterWidget::EventFilter::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent* mouseEvent = static_cast(event); currentState = WaitingForDragLeave; lastKnownMousePosition = mouseEvent->globalPos(); + return true; } else if (event->type() == QEvent::MouseButtonRelease) { QMouseEvent* mouseEvent = static_cast(event); + QAbstractButton* startingButton = static_cast(obj); //If we never left, toggle the original button. if( currentState == WaitingForDragLeave ) { - QAbstractButton* btn = static_cast(obj); - btn->click(); + if ( startingButton->group() && (mouseEvent->modifiers() & Qt::SHIFT)) { + KisColorLabelButtonGroup* const group = static_cast(startingButton->group()); + const QList viableCheckedButtons = group->checkedViableButtons(); + const int buttonsEnabled = viableCheckedButtons.count(); + const bool shouldChangeIsolation = (buttonsEnabled == 1) && (viableCheckedButtons.first() == startingButton); + const bool shouldIsolate = (buttonsEnabled != 1) || !shouldChangeIsolation; + Q_FOREACH(QAbstractButton* otherBtn, group->viableButtons()) { + if (otherBtn == startingButton){ + startingButton->setChecked(true); + } else { + otherBtn->setChecked(!shouldIsolate); + } + } + } else { + startingButton->click(); + } } currentState = Idle; lastKnownMousePosition = mouseEvent->globalPos(); return true; } else if (event->type() == QEvent::MouseMove ) { if (currentState == WaitingForDragLeave) { QMouseEvent* mouseEvent = static_cast(event); QWidget* firstClicked = static_cast(obj); const QPointF localPosition = mouseEvent->localPos(); - if (!firstClicked->rect().contains(localPosition.x(), localPosition.y())) - { + if (!firstClicked->rect().contains(localPosition.x(), localPosition.y())) { QAbstractButton* btn = static_cast(obj); btn->click(); checkSlideOverNeighborButtons(mouseEvent, btn); currentState = WaitingForDragEnter; } lastKnownMousePosition = mouseEvent->globalPos(); return true; } else if (currentState == WaitingForDragEnter) { QMouseEvent* mouseEvent = static_cast(event); QAbstractButton* startingButton = static_cast(obj); const QPoint currentPosition = mouseEvent->globalPos(); checkSlideOverNeighborButtons(mouseEvent, startingButton); lastKnownMousePosition = currentPosition; return true; } } return false; } void KisLayerFilterWidget::EventFilter::checkSlideOverNeighborButtons(QMouseEvent* mouseEvent, QAbstractButton* startingButton) { const QPoint currentPosition = mouseEvent->globalPos(); if (startingButton->group()) { QList allButtons = startingButton->group()->buttons(); Q_FOREACH(QAbstractButton* button, allButtons) { const QRect bounds = QRect(button->mapToGlobal(QPoint(0,0)), button->size()); const QPoint upperLeft = QPoint(qMin(lastKnownMousePosition.x(), currentPosition.x()), qMin(lastKnownMousePosition.y(), currentPosition.y())); const QPoint lowerRight = QPoint(qMax(lastKnownMousePosition.x(), currentPosition.x()), qMax(lastKnownMousePosition.y(), currentPosition.y())); const QRect mouseMovement = QRect(upperLeft, lowerRight); if( bounds.intersects(mouseMovement) && !bounds.contains(lastKnownMousePosition)) { button->click(); } } } }