diff --git a/libs/ui/widgets/kis_color_label_button.cpp b/libs/ui/widgets/kis_color_label_button.cpp index 11d879dbbd..f0d8268ad1 100644 --- a/libs/ui/widgets/kis_color_label_button.cpp +++ b/libs/ui/widgets/kis_color_label_button.cpp @@ -1,394 +1,426 @@ /* * Copyright (c) 2020 Eoin O'Neill * Copyright (c) 2020 Emmet O'Neill * * 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_label_button.h" #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "krita_container_utils.h" struct KisColorLabelButton::Private { const QColor m_color; const uint m_sizeSquared; KisColorLabelButton::SelectionIndicationType selectionVis; Private(QColor color, uint sizeSquared) : m_color(color) , m_sizeSquared(sizeSquared) , selectionVis(KisColorLabelButton::FillIn) { } Private(const Private& rhs) : m_color(rhs.m_color) , m_sizeSquared(rhs.m_sizeSquared) { } }; KisColorLabelButton::KisColorLabelButton(QColor color, uint sizeSquared, QWidget *parent) : QAbstractButton(parent), m_d(new Private(color, sizeSquared)) { setCheckable(true); setChecked(true); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } KisColorLabelButton::~KisColorLabelButton() {} void KisColorLabelButton::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); QStylePainter painter(this); QStyleOptionButton styleOption; styleOption.initFrom(this); - const bool darkTheme = styleOption.palette.background().color().value() < 128; + const bool darkTheme = styleOption.palette.window().color().value() < 128; if (isDown() || isChecked()){ styleOption.state |= QStyle::State_On; } QRect fillRect = kisGrowRect(rect(), -2); QRect outlineRect = kisGrowRect(fillRect, -1); - const QColor shadowColor = styleOption.palette.background().color().darker(128); + const QColor shadowColor = styleOption.palette.window().color().darker(darkTheme ? 128 : 200); const QBrush shadowBrush = QBrush(shadowColor); - const QBrush bgBrush = QBrush(styleOption.palette.background().color()); + const QBrush bgBrush = QBrush(styleOption.palette.window().color()); if (!isEnabled()) { fillRect -= QMargins(sizeHint().width() / 4, sizeHint().height() / 4, sizeHint().width() / 4, sizeHint().height() / 4); } else { fillRect = kisGrowRect(fillRect, -3); } if (m_d->m_color.alpha() > 0) { QColor fillColor = m_d->m_color; if ((!isChecked() || !isEnabled()) && (m_d->selectionVis == FillIn)) { fillColor.setAlpha(32); } else if ((!isChecked() || !isEnabled()) && (m_d->selectionVis == Outline)) { if ((styleOption.state & QStyle::State_MouseOver) == 0) { fillColor.setAlpha(192); } } if ((isEnabled() && isChecked() && m_d->selectionVis == FillIn) || m_d->selectionVis == Outline) { painter.fillRect(kisGrowRect(fillRect, 1), shadowBrush); painter.fillRect(fillRect, bgBrush); } QBrush brush = QBrush(fillColor); painter.fillRect(fillRect, brush); if ((isEnabled() && (m_d->selectionVis == FillIn)) || (isChecked() && (m_d->selectionVis == Outline))) { const QRect& shadowRect = outlineRect; painter.setPen(QPen(shadowColor, 4)); painter.drawRect(shadowRect); painter.setPen(QPen(bgBrush.color(), 2)); painter.drawRect(outlineRect); painter.setPen(QPen(m_d->m_color, 2)); painter.drawRect(outlineRect); } } else { QColor white = QColor(255,255,255); QColor grey = QColor(200,200,200); + QColor xOverlayColor = QColor(100,100,100); QColor outlineColor = grey; if ((!isChecked() || !isEnabled()) && (m_d->selectionVis == FillIn)) { white.setAlpha(32); grey.setAlpha(32); + + if (darkTheme) { + xOverlayColor = styleOption.palette.window().color().lighter(130); + } else { + xOverlayColor = xOverlayColor.lighter(190); + } + } else if ((!isChecked() || !isEnabled()) && (m_d->selectionVis == Outline)) { if ((styleOption.state & QStyle::State_MouseOver) == 0) { white.setAlpha(192); - grey = QColor(125,125,125,192); + xOverlayColor.setAlpha(192); + if (darkTheme) { + grey = grey.darker(110); + } else { + grey = grey.lighter(110); + } + grey.setAlpha(192); } } QBrush whiteBrush = QBrush(white); QBrush greyBrush = QBrush(grey); QRect upperLeftGrey = fillRect - QMargins(0, 0, fillRect.size().width() / 2, fillRect.size().height() /2); QRect lowerRightGrey = fillRect - QMargins(fillRect.size().width() / 2, fillRect.size().height() / 2, 0, 0); + QRect xOverlay = kisGrowRect(fillRect, (m_d->m_sizeSquared / 8) * -1); - if ((isEnabled() && isChecked() && m_d->selectionVis == FillIn) || - m_d->selectionVis == Outline) { - painter.fillRect(fillRect.translated(1,1), shadowBrush); + if (isEnabled() && ((isChecked() && m_d->selectionVis == FillIn) || + m_d->selectionVis == Outline)) { + painter.fillRect(kisGrowRect(fillRect, 1), shadowBrush); painter.fillRect(fillRect, bgBrush); } painter.fillRect(fillRect, whiteBrush); painter.fillRect(upperLeftGrey, greyBrush); painter.fillRect(lowerRightGrey, greyBrush); + painter.setPen(QPen(xOverlayColor, 2)); + painter.drawLine(xOverlay.topLeft(), xOverlay.bottomRight()); + painter.drawLine(xOverlay.bottomLeft(), xOverlay.topRight()); + if ((isEnabled() && (m_d->selectionVis == FillIn)) || (isChecked() && (m_d->selectionVis == Outline))) { const QRect& shadowRect = outlineRect; painter.setPen(QPen(shadowColor, 4)); painter.drawRect(shadowRect); painter.setPen(QPen(bgBrush.color(), 2)); painter.drawRect(outlineRect); painter.setPen(QPen(outlineColor, 2)); painter.drawRect(outlineRect); } } } void KisColorLabelButton::enterEvent(QEvent *event) { + Q_UNUSED(event); update(); } void KisColorLabelButton::leaveEvent(QEvent *event) { + Q_UNUSED(event); update(); } QSize KisColorLabelButton::sizeHint() const { return QSize(m_d->m_sizeSquared,m_d->m_sizeSquared); } void KisColorLabelButton::setSelectionVisType(KisColorLabelButton::SelectionIndicationType type) { m_d->selectionVis = type; } void KisColorLabelButton::nextCheckState() { KisColorLabelFilterGroup* colorLabelFilterGroup = dynamic_cast(group()); - if (!colorLabelFilterGroup || (colorLabelFilterGroup->countCheckedViableButtons() > 1 || !isChecked())) { + if (!colorLabelFilterGroup || (colorLabelFilterGroup->countCheckedViableButtons() > colorLabelFilterGroup->minimumRequiredChecked() || !isChecked())) { setChecked(!isChecked()); } else { setChecked(isChecked()); } } KisColorLabelFilterGroup::KisColorLabelFilterGroup(QObject *parent) : QButtonGroup(parent) + , minimumCheckedButtons(1) { } KisColorLabelFilterGroup::~KisColorLabelFilterGroup() { } QList KisColorLabelFilterGroup::viableButtons() const { QList viableButtons; Q_FOREACH( int index, viableColorLabels ) { viableButtons.append(button(index)); } return viableButtons; } void KisColorLabelFilterGroup::setViableLabels(const QSet &labels) { setAllVisibility(false); disableAll(); QSet removed = viableColorLabels.subtract(labels); viableColorLabels = labels; if (viableColorLabels.count() > 1) { setAllVisibility(true); Q_FOREACH( int index, viableColorLabels) { if (button(index)) { button(index)->setEnabled(true); } } } Q_FOREACH( int index, removed ) { button(index)->setChecked(true); } } void KisColorLabelFilterGroup::setViableLabels(const QList &viableLabels) { setViableLabels(QSet::fromList(viableLabels)); } QSet KisColorLabelFilterGroup::getActiveLabels() const { QSet checkedLabels = QSet(); Q_FOREACH( int index, viableColorLabels ) { if (button(index)->isChecked()) { checkedLabels.insert(index); } } return checkedLabels.count() == viableColorLabels.count() ? QSet() : checkedLabels; } QList KisColorLabelFilterGroup::checkedViableButtons() const { QList checkedButtons = viableButtons(); KritaUtils::filterContainer(checkedButtons, [](QAbstractButton* btn){ return (btn->isChecked()); }); return checkedButtons; } int KisColorLabelFilterGroup::countCheckedViableButtons() const { return checkedViableButtons().count(); } int KisColorLabelFilterGroup::countViableButtons() const { return viableColorLabels.count(); } +void KisColorLabelFilterGroup::setMinimumRequiredChecked(int checkedBtns) +{ + minimumCheckedButtons = checkedBtns; +} + +int KisColorLabelFilterGroup::minimumRequiredChecked() +{ + return minimumCheckedButtons; +} + void KisColorLabelFilterGroup::reset() { Q_FOREACH( QAbstractButton* btn, viableButtons() ) { btn->setChecked(true); } } void KisColorLabelFilterGroup::disableAll() { Q_FOREACH( QAbstractButton* btn, buttons() ) { btn->setDisabled(true); } } void KisColorLabelFilterGroup::setAllVisibility(const bool vis) { Q_FOREACH( QAbstractButton* btn, buttons() ) { btn->setVisible(vis); } } KisColorLabelMouseDragFilter::KisColorLabelMouseDragFilter(QObject* parent) : QObject(parent) { lastKnownMousePosition = QPoint(0,0); currentState = Idle; } bool KisColorLabelMouseDragFilter::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) { 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 ) { if ( startingButton->group() && (mouseEvent->modifiers() & Qt::SHIFT)) { KisColorLabelFilterGroup* 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())) { 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 KisColorLabelMouseDragFilter::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(); } } } } diff --git a/libs/ui/widgets/kis_color_label_button.h b/libs/ui/widgets/kis_color_label_button.h index e92830412d..b588587f8a 100644 --- a/libs/ui/widgets/kis_color_label_button.h +++ b/libs/ui/widgets/kis_color_label_button.h @@ -1,98 +1,102 @@ /* * Copyright (c) 2020 Eoin O'Neill * Copyright (c) 2020 Emmet O'Neill * * 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 KISCOLORLABELBUTTON_H #define KISCOLORLABELBUTTON_H #include #include #include #include "kritaui_export.h" class KRITAUI_EXPORT KisColorLabelButton : public QAbstractButton { Q_OBJECT public: enum SelectionIndicationType { FillIn, Outline }; KisColorLabelButton(QColor color, uint sizeSquared = 32, QWidget *parent = nullptr); ~KisColorLabelButton(); void paintEvent(QPaintEvent* event) override; void enterEvent(QEvent *event) override; void leaveEvent(QEvent *event) override; QSize sizeHint() const override; void setSelectionVisType( SelectionIndicationType type ); virtual void nextCheckState() override; private: struct Private; const QScopedPointer m_d; }; class KRITAUI_EXPORT KisColorLabelFilterGroup : public QButtonGroup { Q_OBJECT public: KisColorLabelFilterGroup(QObject* parent); ~KisColorLabelFilterGroup(); QList viableButtons() const; void setViableLabels(const QSet &buttons); void setViableLabels(const QList &viableLabels); QSet getActiveLabels() const; QList checkedViableButtons() const; int countCheckedViableButtons() const; int countViableButtons() const; + void setMinimumRequiredChecked( int checkedBtns ); + int minimumRequiredChecked(); + public Q_SLOTS: void reset(); void setAllVisibility(const bool vis); private: void disableAll(); QSet viableColorLabels; + int minimumCheckedButtons; }; class KRITAUI_EXPORT KisColorLabelMouseDragFilter : public QObject { enum State{ Idle, WaitingForDragLeave, //Waiting for mouse to exit first clicked while the mouse button is down. WaitingForDragEnter //Waiting for mouse to slide across buttons within the same button group. }; State currentState; QPoint lastKnownMousePosition; public: KisColorLabelMouseDragFilter(QObject *parent = nullptr); protected: bool eventFilter(QObject *obj, QEvent *event); void checkSlideOverNeighborButtons(QMouseEvent* mouseEvent, class QAbstractButton* startingButton); }; #endif // KISCOLORLABELBUTTON_H diff --git a/libs/ui/widgets/kis_color_label_selector_widget.cpp b/libs/ui/widgets/kis_color_label_selector_widget.cpp index 7d3f9c11c3..ee2225ad70 100644 --- a/libs/ui/widgets/kis_color_label_selector_widget.cpp +++ b/libs/ui/widgets/kis_color_label_selector_widget.cpp @@ -1,175 +1,169 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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_label_selector_widget.h" #include "kis_debug.h" #include "kis_global.h" #include #include #include #include #include #include #include #include #include "kis_color_label_button.h" #include "kis_node_view_color_scheme.h" struct Private { Private(KisColorLabelSelectorWidget *_q) : q(_q) - , buttonSize(24) + , buttonSize(26) { } KisColorLabelSelectorWidget *q; QVector colors; QButtonGroup* colorButtonGroup; QSpacerItem* menuAlignmentOffset; const int buttonSize; }; KisColorLabelSelectorWidget::KisColorLabelSelectorWidget(QWidget *parent) : QWidget(parent) , m_d(new Private(this)) { KisNodeViewColorScheme scm; m_d->colors = scm.allColorLabels(); QHBoxLayout *layout = new QHBoxLayout(this); this->setLayout(layout); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); layout->setAlignment(Qt::AlignLeft); m_d->menuAlignmentOffset = new QSpacerItem(0,0); layout->addItem(m_d->menuAlignmentOffset); { m_d->colorButtonGroup = new QButtonGroup(this); m_d->colorButtonGroup->setExclusive(true); for (int id = 0; id < m_d->colors.count(); id++) { KisColorLabelButton* btn = new KisColorLabelButton(m_d->colors[id], m_d->buttonSize, this); btn->setChecked(false); btn->setSelectionVisType(KisColorLabelButton::Outline); m_d->colorButtonGroup->addButton(btn, id); layout->addWidget(btn); } connect(m_d->colorButtonGroup, SIGNAL(buttonToggled(int,bool)), this, SLOT(groupButtonChecked(int,bool))); } } KisColorLabelSelectorWidget::~KisColorLabelSelectorWidget(){ } int KisColorLabelSelectorWidget::currentIndex() const { return m_d->colorButtonGroup->checkedId(); } QSize KisColorLabelSelectorWidget::sizeHint() const { return QSize(calculateMenuOffset() + m_d->buttonSize * m_d->colors.count(), m_d->buttonSize); } void KisColorLabelSelectorWidget::resizeEvent(QResizeEvent *e) { int menuOffset = calculateMenuOffset(); m_d->menuAlignmentOffset->changeSize(menuOffset, height()); layout()->invalidate(); QMenu *menu = qobject_cast(parent()); if(menu) { menu->resize(menu->width() + menuOffset, menu->height()); } QWidget::resizeEvent(e); } int KisColorLabelSelectorWidget::calculateMenuOffset() const { bool hasWideItems = false; QMenu *menu = qobject_cast(parent()); bool hasCheckable = false; bool hasIcon = false; int menuOffset = 0; if (menu) { Q_FOREACH(QAction *action, menu->actions()) { hasCheckable |= action->isCheckable(); hasIcon |= action->icon().isNull(); hasWideItems |= (hasCheckable || hasIcon); - // Ideally, we would early terminate, - // but we don't know if we need both icons - // and checkbox space until we traverse - // the whole menu. Previous behavior caused - // a bug on KDE systems which provide icons - // and checkboxes. - if (hasCheckable && hasIcon) { + if (hasWideItems) { break; } } } if (hasWideItems) { QStyleOption opt; opt.init(this); // some copy-pasted code from QFusionStyle style - const int hmargin = style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this); - const int icone = style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, this); - menuOffset = (hmargin + icone + 6) * (hasCheckable + hasIcon); + const int hMargin = style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this); + const int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, this); + menuOffset = (hMargin + iconSize + 6); } return menuOffset; } void KisColorLabelSelectorWidget::groupButtonChecked(int index, bool state) { if (state == true) { emit currentIndexChanged(index); } } void KisColorLabelSelectorWidget::setCurrentIndex(int index) { if (index == -1) { QAbstractButton* btn = m_d->colorButtonGroup->checkedButton(); if (btn) { btn->group()->setExclusive(false); btn->setChecked(false); btn->group()->setExclusive(true); } } else if (index != m_d->colorButtonGroup->checkedId()) { QAbstractButton* btn = m_d->colorButtonGroup->button(index); if (btn) { btn->setChecked(true); } } emit currentIndexChanged(index); } diff --git a/libs/ui/widgets/kis_layer_filter_widget.cpp b/libs/ui/widgets/kis_layer_filter_widget.cpp index 74abe3e29e..954deb7bab 100644 --- a/libs/ui/widgets/kis_layer_filter_widget.cpp +++ b/libs/ui/widgets/kis_layer_filter_widget.cpp @@ -1,293 +1,299 @@ /* * Copyright (c) 2020 Eoin O'Neill * Copyright (c) 2020 Emmet O'Neill * * 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_layer_filter_widget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "kis_node.h" #include "kis_global.h" #include "kis_icon_utils.h" #include "kis_color_filter_combo.h" #include "kis_color_label_button.h" #include "kis_color_label_selector_widget.h" #include "kis_node_view_color_scheme.h" #include "KisMouseClickEater.h" KisLayerFilterWidget::KisLayerFilterWidget(QWidget *parent) : QWidget(parent) { QVBoxLayout *layout = new QVBoxLayout(this); setLayout(layout); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); textFilter = new QLineEdit(this); textFilter->setPlaceholderText(i18n("Filter by name...")); textFilter->setMinimumWidth(255); textFilter->setMinimumHeight(28); textFilter->setClearButtonEnabled(true); connect(textFilter, SIGNAL(textChanged(QString)), this, SIGNAL(filteringOptionsChanged())); + connect(textFilter, &QLineEdit::returnPressed, [this]() { + QMenu* menu = dynamic_cast(parentWidget()); + if (menu) { + menu->close(); + } + }); KisNodeViewColorScheme colorScheme; QWidget* buttonContainer = new QWidget(this); MouseClickIgnore* mouseEater = new MouseClickIgnore(this); buttonContainer->setToolTip(i18n("Filter by color label...")); buttonContainer->installEventFilter(mouseEater); buttonEventFilter = new KisColorLabelMouseDragFilter(buttonContainer); { QHBoxLayout *subLayout = new QHBoxLayout(buttonContainer); buttonContainer->setLayout(subLayout); subLayout->setContentsMargins(0,0,0,0); subLayout->setSpacing(0); subLayout->setAlignment(Qt::AlignLeft); buttonGroup = new KisColorLabelFilterGroup(buttonContainer); buttonGroup->setExclusive(false); QVector colors = colorScheme.allColorLabels(); for (int id = 0; id < colors.count(); id++) { KisColorLabelButton* btn = new KisColorLabelButton(colors[id], 28, buttonContainer); buttonGroup->addButton(btn, id); btn->installEventFilter(buttonEventFilter); subLayout->addWidget(btn); } connect(buttonGroup, SIGNAL(buttonToggled(int,bool)), this, SIGNAL(filteringOptionsChanged())); } resetButton = new QPushButton(i18n("Reset Filters"), this); resetButton->setMinimumHeight(28); connect(resetButton, &QPushButton::clicked, [this](){ this->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); buttonGroup->setViableLabels(colorLabels); } bool KisLayerFilterWidget::isCurrentlyFiltering() const { const bool isFilteringText = hasTextFilter(); const bool isFilteringColors = buttonGroup->getActiveLabels().count() > 0; return isFilteringText || isFilteringColors; } bool KisLayerFilterWidget::hasTextFilter() const { return !textFilter->text().isEmpty(); } QSet KisLayerFilterWidget::getActiveColors() const { QSet activeColors = buttonGroup->getActiveLabels(); return activeColors; } QString KisLayerFilterWidget::getTextFilter() const { return textFilter->text(); } int KisLayerFilterWidget::getDesiredMinimumWidth() const { return qMax(textFilter->minimumWidth(), buttonGroup->countViableButtons() * 32); } int KisLayerFilterWidget::getDesiredMinimumHeight() const { QList viableButtons = buttonGroup->viableButtons(); if (viableButtons.count() > 1) { return viableButtons[0]->sizeHint().height() + textFilter->minimumHeight() + resetButton->minimumHeight(); } else { return textFilter->minimumHeight() + resetButton->minimumHeight(); } } void KisLayerFilterWidget::reset() { textFilter->clear(); buttonGroup->reset(); filteringOptionsChanged(); } QSize KisLayerFilterWidget::sizeHint() const { return QSize(getDesiredMinimumWidth(), getDesiredMinimumHeight()); } void KisLayerFilterWidget::showEvent(QShowEvent *show) { QMenu *parentMenu = dynamic_cast(parentWidget()); if (parentMenu) { const int widthBefore = parentMenu->width(); const int rightEdgeThreshold = 5; //Fake resize event needs to be made to register change in widget menu size. //Not doing this will cause QMenu to not resize properly! resize(sizeHint()); adjustSize(); QResizeEvent event = QResizeEvent(sizeHint(), parentMenu->size()); parentMenu->resize(sizeHint()); parentMenu->adjustSize(); qApp->sendEvent(parentMenu, &event); QScreen *screen = QGuiApplication::screenAt(parentMenu->mapToGlobal(parentMenu->pos())); QRect screenGeometry = screen ? screen->geometry() : parentMenu->parentWidget()->window()->geometry(); const bool onRightEdge = (parentMenu->pos().x() + widthBefore + rightEdgeThreshold) > screenGeometry.width(); const int widthAfter = parentMenu->width(); if (onRightEdge) { if (widthAfter > widthBefore) { const QRect newGeo = kisEnsureInRect( parentMenu->geometry(), screenGeometry ); const int xShift = newGeo.x() - parentMenu->pos().x(); parentMenu->move(parentMenu->pos().x() + xShift, parentMenu->pos().y() + 0); } else { const int xShift = widthBefore - widthAfter; parentMenu->move(parentMenu->pos().x() + xShift, parentMenu->pos().y() + 0); } } } QWidget::showEvent(show); } KisLayerFilterWidgetToolButton::KisLayerFilterWidgetToolButton(QWidget *parent) : QToolButton(parent) { m_textFilter = false; m_selectedColors = QList(); } KisLayerFilterWidgetToolButton::KisLayerFilterWidgetToolButton(const KisLayerFilterWidgetToolButton &rhs) : QToolButton(rhs.parentWidget()) , m_textFilter(rhs.m_textFilter) , m_selectedColors(rhs.m_selectedColors) { } void KisLayerFilterWidgetToolButton::setSelectedColors(QList colors) { m_selectedColors = colors; } void KisLayerFilterWidgetToolButton::setTextFilter(bool isTextFiltering) { m_textFilter = isTextFiltering; } void KisLayerFilterWidgetToolButton::paintEvent(QPaintEvent *paintEvent) { KisNodeViewColorScheme colorScheme; const bool validColorFilter = !(m_selectedColors.count() == 0 || m_selectedColors.count() == colorScheme.allColorLabels().count()); if (m_textFilter == false && !validColorFilter) { QToolButton::paintEvent(paintEvent); } else { QStylePainter paint(this); QStyleOptionToolButton opt; initStyleOption(&opt); opt.icon = m_textFilter ? KisIconUtils::loadIcon("format-text-bold") : icon(); paint.drawComplexControl(QStyle::CC_ToolButton, opt); const QSize halfIconSize = this->iconSize() / 2; const QSize halfButtonSize = this->size() / 2; const QRect editRect = kisGrowRect(QRect(QPoint(halfButtonSize.width() - halfIconSize.width(), halfButtonSize.height() - halfIconSize.height()),this->iconSize()), -1); const int size = qMin(editRect.width(), editRect.height()); if( validColorFilter ) { KisColorFilterCombo::paintColorPie(paint, opt.palette, m_selectedColors, editRect, size ); if (m_textFilter) { if (!opt.icon.isNull()) { QRadialGradient radGradient = QRadialGradient(editRect.center(), size); QColor shadowTransparent = palette().shadow().color(); shadowTransparent.setAlpha(96); radGradient.setColorAt(0.0f, shadowTransparent); shadowTransparent.setAlpha(0); radGradient.setColorAt(1.0f, shadowTransparent); paint.setBrush(radGradient); paint.setPen(Qt::NoPen); paint.drawEllipse(editRect.center(), size, size); opt.icon.paint(&paint, editRect); } } } } } MouseClickIgnore::MouseClickIgnore(QObject *parent) : QObject(parent) { } bool MouseClickIgnore::eventFilter(QObject *obj, QEvent *event) { if (obj && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick || event->type() == QEvent::MouseButtonRelease)) { event->setAccepted(true); return true; } else { return false; } } diff --git a/plugins/dockers/animation/onion_skins_docker.cpp b/plugins/dockers/animation/onion_skins_docker.cpp index dc80e8b2bd..2d58854ed9 100644 --- a/plugins/dockers/animation/onion_skins_docker.cpp +++ b/plugins/dockers/animation/onion_skins_docker.cpp @@ -1,236 +1,237 @@ /* * Copyright (c) 2015 Jouni Pentikäinen * * 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 "onion_skins_docker.h" #include "ui_onion_skins_docker.h" #include #include #include #include #include "kis_icon_utils.h" #include "kis_image_config.h" #include "kis_onion_skin_compositor.h" #include "kis_signals_blocker.h" #include "kis_node_view_color_scheme.h" #include "KisViewManager.h" #include "kis_action_manager.h" #include "kis_action.h" #include #include "kis_equalizer_widget.h" #include "kis_color_label_button.h" OnionSkinsDocker::OnionSkinsDocker(QWidget *parent) : QDockWidget(i18n("Onion Skins"), parent), ui(new Ui::OnionSkinsDocker), m_updatesCompressor(300, KisSignalCompressor::FIRST_ACTIVE), m_toggleOnionSkinsAction(0) { QWidget* mainWidget = new QWidget(this); setWidget(mainWidget); KisImageConfig config(true); ui->setupUi(mainWidget); mainWidget->setContentsMargins(10, 10, 10, 10); ui->doubleTintFactor->setMinimum(0); ui->doubleTintFactor->setMaximum(100); ui->doubleTintFactor->setPrefix(i18n("Tint: ")); ui->doubleTintFactor->setSuffix(i18n("%")); ui->btnBackwardColor->setToolTip(i18n("Tint color for past frames")); ui->btnForwardColor->setToolTip(i18n("Tint color for future frames")); QVBoxLayout *layout = ui->slidersLayout; m_equalizerWidget = new KisEqualizerWidget(10, this); connect(m_equalizerWidget, SIGNAL(sigConfigChanged()), &m_updatesCompressor, SLOT(start())); layout->addWidget(m_equalizerWidget, 1); connect(ui->btnBackwardColor, SIGNAL(changed(KoColor)), &m_updatesCompressor, SLOT(start())); connect(ui->btnForwardColor, SIGNAL(changed(KoColor)), &m_updatesCompressor, SLOT(start())); connect(ui->doubleTintFactor, SIGNAL(valueChanged(qreal)), &m_updatesCompressor, SLOT(start())); connect(&m_updatesCompressor, SIGNAL(timeout()), SLOT(changed())); { const bool isShown = config.showAdditionalOnionSkinsSettings(); ui->btnShowHide->setChecked(isShown); connect(ui->btnShowHide, SIGNAL(toggled(bool)), SLOT(slotShowAdditionalSettings(bool))); slotShowAdditionalSettings(isShown); } { KisNodeViewColorScheme scm; m_filterButtonGroup = new KisColorLabelFilterGroup(this); m_dragFilter = new KisColorLabelMouseDragFilter(this); m_filterButtonGroup->setExclusive(false); + m_filterButtonGroup->setMinimumRequiredChecked(0); QWidget* filterButtonContainer = ui->colorFilterGroupbox; QLayout* filterButtonLayout = ui->filterButtons; filterButtonLayout->setSpacing(0); QVector availableColors = scm.allColorLabels(); QSet viableColors; for (int i = 0; i < availableColors.count(); i++) { KisColorLabelButton* colorLabelButton = new KisColorLabelButton(availableColors[i], 24, filterButtonContainer); filterButtonLayout->addWidget(colorLabelButton); m_filterButtonGroup->addButton(colorLabelButton, i); colorLabelButton->installEventFilter(m_dragFilter); viableColors << i; } m_filterButtonGroup->setViableLabels(viableColors); connect(m_filterButtonGroup, SIGNAL(buttonToggled(int,bool)), this, SLOT(slotFilteredColorsChanged())); connect(ui->colorFilterGroupbox, SIGNAL(toggled(bool)), this, SLOT(slotFilteredColorsChanged())); connect(ui->resetFilter, SIGNAL(pressed()), m_filterButtonGroup, SLOT(reset()) ); } loadSettings(); KisOnionSkinCompositor::instance()->configChanged(); // this mostly hides the checkboxes since no filtering is done by default slotFilteredColorsChanged(); resize(sizeHint()); } OnionSkinsDocker::~OnionSkinsDocker() { delete ui; } void OnionSkinsDocker::setCanvas(KoCanvasBase *canvas) { Q_UNUSED(canvas); } void OnionSkinsDocker::unsetCanvas() { } void OnionSkinsDocker::setViewManager(KisViewManager *view) { KisActionManager *actionManager = view->actionManager(); m_toggleOnionSkinsAction = actionManager->createAction("toggle_onion_skin"); connect(m_toggleOnionSkinsAction, SIGNAL(triggered()), SLOT(slotToggleOnionSkins())); slotUpdateIcons(); connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateIcons())); } void OnionSkinsDocker::slotToggleOnionSkins() { m_equalizerWidget->toggleMasterSwitch(); } void OnionSkinsDocker::slotFilteredColorsChanged() { // what colors are selected to filter?? QSet selectedFilterColors = m_filterButtonGroup->getActiveLabels(); // show all colors if the filter is off and ignore the checkboxes if(ui->colorFilterGroupbox->isChecked() == false) { selectedFilterColors.clear(); selectedFilterColors << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8; // show everything } m_filterButtonGroup->setAllVisibility(ui->colorFilterGroupbox->isChecked()); ui->resetFilter->setVisible(ui->colorFilterGroupbox->isChecked()); // existing code KisOnionSkinCompositor::instance()->setColorLabelFilter(QList::fromSet(selectedFilterColors)); KisOnionSkinCompositor::instance()->configChanged(); } void OnionSkinsDocker::slotUpdateIcons() { if (m_toggleOnionSkinsAction) { m_toggleOnionSkinsAction->setIcon(KisIconUtils::loadIcon("onion_skin_options")); } } void OnionSkinsDocker::slotShowAdditionalSettings(bool value) { ui->lblPrevColor->setVisible(value); ui->lblNextColor->setVisible(value); ui->btnBackwardColor->setVisible(value); ui->btnForwardColor->setVisible(value); ui->doubleTintFactor->setVisible(value); QIcon icon = KisIconUtils::loadIcon(value ? "arrow-down" : "arrow-up"); ui->btnShowHide->setIcon(icon); KisImageConfig(false).setShowAdditionalOnionSkinsSettings(value); } void OnionSkinsDocker::changed() { KisImageConfig config(false); KisEqualizerWidget::EqualizerValues v = m_equalizerWidget->getValues(); config.setNumberOfOnionSkins(v.maxDistance); for (int i = -v.maxDistance; i <= v.maxDistance; i++) { config.setOnionSkinOpacity(i, v.value[i] * 255.0 / 100.0); config.setOnionSkinState(i, v.state[i]); } config.setOnionSkinTintFactor(ui->doubleTintFactor->value() * 255.0 / 100.0); config.setOnionSkinTintColorBackward(ui->btnBackwardColor->color().toQColor()); config.setOnionSkinTintColorForward(ui->btnForwardColor->color().toQColor()); KisOnionSkinCompositor::instance()->configChanged(); } void OnionSkinsDocker::loadSettings() { KisImageConfig config(true); KisSignalsBlocker b(ui->doubleTintFactor, ui->btnBackwardColor, ui->btnForwardColor, m_equalizerWidget); ui->doubleTintFactor->setValue(qRound(config.onionSkinTintFactor() * 100.0 / 255)); KoColor bcol(KoColorSpaceRegistry::instance()->rgb8()); bcol.fromQColor(config.onionSkinTintColorBackward()); ui->btnBackwardColor->setColor(bcol); bcol.fromQColor(config.onionSkinTintColorForward()); ui->btnForwardColor->setColor(bcol); KisEqualizerWidget::EqualizerValues v; v.maxDistance = 10; for (int i = -v.maxDistance; i <= v.maxDistance; i++) { v.value.insert(i, qRound(config.onionSkinOpacity(i) * 100.0 / 255.0)); v.state.insert(i, config.onionSkinState(i)); } m_equalizerWidget->setValues(v); }