Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_color_label_button.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Eoin O 'Neill <eoinoneill1991@gmail.com>
3 * SPDX-FileCopyrightText: 2020 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
8
9#include <QStylePainter>
10#include <QStyleOption>
11#include <QMimeData>
12#include <QMouseEvent>
13
14
15#include "kis_global.h"
16#include "kis_debug.h"
18
20{
21 const QColor m_color;
24
25 Private(QColor color, uint sizeSquared)
26 : m_color(color)
27 , m_sizeSquared(sizeSquared)
29 {
30
31 }
32
33 Private(const Private& rhs)
34 : m_color(rhs.m_color)
37 {
38
39 }
40};
41
42KisColorLabelButton::KisColorLabelButton(QColor color, uint sizeSquared, QWidget *parent) : QAbstractButton(parent), m_d(new Private(color, sizeSquared))
43{
44 setCheckable(true);
45 setChecked(true);
46 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
47}
48
50
51void KisColorLabelButton::paintEvent(QPaintEvent *event)
52{
53 QWidget::paintEvent(event);
54
55 QStylePainter painter(this);
56 QStyleOptionButton styleOption;
57 styleOption.initFrom(this);
58
59 if (isDown() || isChecked()){
60 styleOption.state |= QStyle::State_On;
61 }
62
63 const QRect rect = this->rect();
64 QRect interiorRect = kisGrowRect(rect, -3);
65 const QColor borderColor = QColor(0, 0, 0, 128);
66
67 if (!isEnabled()) {
68 const int w = qMin(interiorRect.width(), qMax(8, interiorRect.width() / 2));
69 const int h = qMin(interiorRect.height(), qMax(8, interiorRect.height() / 2));
70 const int marginX = (interiorRect.width() - w) / 2;
71 const int marginY = (interiorRect.height() - h) / 2;
72 interiorRect.adjust(marginX, marginY, -marginX, -marginY);
73 }
74
75 if (m_d->m_color.alpha() > 0) {
76 QColor fillColor = m_d->m_color;
77
78 painter.setBrush(Qt::NoBrush);
79 painter.setPen(QPen(borderColor, 1));
80
81 if (!isEnabled()) {
82 fillColor.setAlpha(32);
83 } else if (!isChecked() && (styleOption.state & QStyle::State_MouseOver) == 0) {
84 fillColor.setAlpha(192);
85 }
86
87 if (isEnabled() && (m_d->selectionVis == FillIn || isChecked())) {
88 painter.fillRect(rect, fillColor);
89 painter.drawRect(rect.adjusted(0, 0, -1, -1));
90 }
91 painter.fillRect(interiorRect,
92 !isEnabled() || m_d->selectionVis == Outline || isChecked()
93 ? fillColor
94 : styleOption.palette.window().color());
95 painter.drawRect(interiorRect.adjusted(0, 0, -1, -1));
96
97 } else {
98 QColor white = QColor(255,255,255);
99 QColor greyOutside = QColor(200,200,200);
100 QColor greyInside = greyOutside;
101 QColor xColor = QColor(0, 0, 0, 128);
102
103 if (!isEnabled()) {
104 white.setAlpha(32);
105 greyInside.setAlpha(32);
106 xColor.setAlpha(32);
107 } else if (!isChecked()) {
108 if ((styleOption.state & QStyle::State_MouseOver) == 0) {
109 greyOutside.setAlpha(128);
110 greyInside.setAlpha(128);
111 } else if (m_d->selectionVis == FillIn) {
112 greyInside.setAlpha(128);
113 }
114 white.setAlpha(128);
115 xColor.setAlpha(64);
116 }
117
118 painter.setBrush(Qt::NoBrush);
119 painter.setPen(QPen(borderColor, 1));
120
121 if (isEnabled() && (m_d->selectionVis == FillIn || isChecked())) {
122 painter.fillRect(QRect(rect.topLeft(), rect.topRight() + QPoint(0, 2)), greyOutside);
123 painter.fillRect(QRect(rect.bottomLeft() + QPoint(0, -2), rect.bottomRight()), greyOutside);
124 painter.fillRect(QRect(rect.topLeft() + QPoint(0, 3), rect.bottomLeft() + QPoint(2, -3)), greyOutside);
125 painter.fillRect(QRect(rect.topRight() + QPoint(-2, 3), rect.bottomRight() + QPoint(0, -3)), greyOutside);
126 painter.drawRect(rect.adjusted(0, 0, -1, -1));
127 }
128 painter.fillRect(interiorRect, greyInside);
129 const int tileWidth = interiorRect.width() / 2;
130 const int tileHeight = interiorRect.height() / 2;
131 painter.fillRect(interiorRect.adjusted(0, 0, -tileWidth, -tileHeight), white);
132 painter.fillRect(interiorRect.adjusted(tileWidth, tileHeight, 0, 0), white);
133 painter.drawRect(interiorRect.adjusted(0, 0, -1, -1));
134 QRect xRect = interiorRect.adjusted(4, 4, -3, -3);
135 while (xRect.width() <= 8 || xRect.height() <= 8) {
136 xRect.adjust(-1, -1, 1, 1);
137 }
138 xRect = xRect.intersected(interiorRect);
139 if (xRect.width() <= 8 || xRect.height() <= 8) {
140 xRect = interiorRect;
141 painter.setPen(QPen(xColor, 1));
142 } else {
143 painter.setPen(QPen(xColor, 2));
144 }
145 painter.drawLine(xRect.topLeft(), xRect.bottomRight());
146 painter.drawLine(xRect.bottomLeft(), xRect.topRight());
147 }
148}
149
150#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
151void KisColorLabelButton::enterEvent(QEvent *event) {
152#else
153void KisColorLabelButton::enterEvent(QEnterEvent *event) {
154#endif
155 Q_UNUSED(event);
156 update();
157}
158
160 Q_UNUSED(event);
161 update();
162}
163
165{
166 return QSize(m_d->m_sizeSquared,m_d->m_sizeSquared);
167}
168
173
178
180{
181 if (size == m_d->m_sizeSquared) {
182 return;
183 }
184 m_d->m_sizeSquared = size;
185 update();
186}
187
189{
190 KisColorLabelFilterGroup* colorLabelFilterGroup = dynamic_cast<KisColorLabelFilterGroup*>(group());
191
192 if (!colorLabelFilterGroup || (colorLabelFilterGroup->countCheckedViableButtons() > colorLabelFilterGroup->minimumRequiredChecked() || !isChecked())) {
193 setChecked(!isChecked());
194 } else {
195 setChecked(isChecked());
196 }
197}
198
200 : QButtonGroup(parent)
201 , minimumCheckedButtons(1)
202{
203}
204
208
211
212 Q_FOREACH( int index, viableColorLabels ) {
213 viableButtons.append(button(index));
214 }
215
216 return viableButtons;
217}
218
219void KisColorLabelFilterGroup::setViableLabels(const QSet<int> &labels) {
220 setAllVisibility(false);
221 disableAll();
222 QSet<int> removed = viableColorLabels.subtract(labels);
223
224 viableColorLabels = labels;
225
226 if (viableColorLabels.count() > 1) {
227 setAllVisibility(true);
228 Q_FOREACH( int index, viableColorLabels) {
229 if (button(index)) {
230 button(index)->setEnabled(true);
231 }
232 }
233 }
234
235 Q_FOREACH( int index, removed ) {
236 button(index)->setChecked(true);
237 }
238}
239
241{
242 QSet<int> uniqueViableLabels;
243 uniqueViableLabels = QSet<int>(viableLabels.cbegin(), viableLabels.cend());
244 setViableLabels(uniqueViableLabels);
245}
246
248 QSet<int> checkedLabels = QSet<int>();
249
250 Q_FOREACH( int index, viableColorLabels ) {
251 if (button(index)->isChecked()) {
252 checkedLabels.insert(index);
253 }
254 }
255
256 return checkedLabels.count() == viableColorLabels.count() && minimumRequiredChecked() > 0 ? QSet<int>() : checkedLabels;
257}
258
260 QList<QAbstractButton*> checkedButtons = viableButtons();
261
262 KritaUtils::filterContainer(checkedButtons, [](QAbstractButton* btn){
263 return (btn->isChecked());
264 });
265
266 return checkedButtons;
267}
268
272
276
278{
279 minimumCheckedButtons = checkedBtns;
280}
281
286
288 Q_FOREACH( QAbstractButton* btn, viableButtons() ) {
289 btn->setChecked(true);
290 }
291}
292
294 Q_FOREACH( QAbstractButton* btn, buttons() ) {
295 btn->setDisabled(true);
296 }
297}
298
300{
301 Q_FOREACH( QAbstractButton* btn, buttons() ) {
302 btn->setVisible(vis);
303 }
304}
305
307{
308 lastKnownMousePosition = QPoint(0,0);
310}
311
312bool KisColorLabelMouseDragFilter::eventFilter(QObject *obj, QEvent *event)
313{
314 if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) {
315 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
316
318 lastKnownMousePosition = mouseEvent->globalPos();
319
320 return true;
321
322 } else if (event->type() == QEvent::MouseButtonRelease) {
323 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
324 QAbstractButton* startingButton = static_cast<QAbstractButton*>(obj);
325
326 //If we never left, toggle the original button.
328 if ( startingButton->group() && (mouseEvent->modifiers() & Qt::SHIFT)) {
329 KisColorLabelFilterGroup* const group = static_cast<KisColorLabelFilterGroup*>(startingButton->group());
330 const QList<QAbstractButton*> viableCheckedButtons = group->checkedViableButtons();
331
332 const int buttonsEnabled = viableCheckedButtons.count();
333 const bool shouldChangeIsolation = (buttonsEnabled == 1) && (viableCheckedButtons.first() == startingButton);
334 const bool shouldIsolate = (buttonsEnabled != 1) || !shouldChangeIsolation;
335
336 Q_FOREACH(QAbstractButton* otherBtn, group->viableButtons()) {
337 if (otherBtn == startingButton){
338 startingButton->setChecked(true);
339 } else {
340 otherBtn->setChecked(!shouldIsolate);
341 }
342 }
343
344 } else {
345 startingButton->click();
346 }
347 }
348
350 lastKnownMousePosition = mouseEvent->globalPos();
351
352 return true;
353
354 } else if (event->type() == QEvent::MouseMove) {
355
357 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
358 QWidget* firstClicked = static_cast<QWidget*>(obj);
359 const QPointF localPosition = mouseEvent->localPos();
360
361 if (!firstClicked->rect().contains(localPosition.x(), localPosition.y())) {
362 QAbstractButton* btn = static_cast<QAbstractButton*>(obj);
363 btn->click();
364
365 checkSlideOverNeighborButtons(mouseEvent, btn);
366
368 }
369
370 lastKnownMousePosition = mouseEvent->globalPos();
371
372 return true;
373
374 } else if (currentState == WaitingForDragEnter) {
375 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
376 QAbstractButton* startingButton = static_cast<QAbstractButton*>(obj);
377 const QPoint currentPosition = mouseEvent->globalPos();
378
379 checkSlideOverNeighborButtons(mouseEvent, startingButton);
380
381 lastKnownMousePosition = currentPosition;
382
383 return true;
384 }
385
386 }
387
388 return false;
389}
390
391void KisColorLabelMouseDragFilter::checkSlideOverNeighborButtons(QMouseEvent* mouseEvent, QAbstractButton* startingButton)
392{
393 const QPoint currentPosition = mouseEvent->globalPos();
394
395 if (startingButton->group()) {
396 QList<QAbstractButton*> allButtons = startingButton->group()->buttons();
397
398 Q_FOREACH(QAbstractButton* button, allButtons) {
399 const QRect bounds = QRect(button->mapToGlobal(QPoint(0,0)), button->size());
400 const QPoint upperLeft = QPoint(qMin(lastKnownMousePosition.x(), currentPosition.x()), qMin(lastKnownMousePosition.y(), currentPosition.y()));
401 const QPoint lowerRight = QPoint(qMax(lastKnownMousePosition.x(), currentPosition.x()), qMax(lastKnownMousePosition.y(), currentPosition.y()));
402 const QRect mouseMovement = QRect(upperLeft, lowerRight);
403 if( bounds.intersects(mouseMovement) && !bounds.contains(lastKnownMousePosition)) {
404 button->click();
405 }
406 }
407 }
408}
unsigned int uint
void paintEvent(QPaintEvent *event) override
void setSelectionVisType(SelectionIndicationType type)
QSize sizeHint() const override
void enterEvent(QEnterEvent *event) override
void leaveEvent(QEvent *event) override
KisColorLabelButton(QColor color, uint sizeSquared=32, QWidget *parent=nullptr)
SelectionIndicationType selectionVisType() const
const QScopedPointer< Private > m_d
void setAllVisibility(const bool vis)
void setViableLabels(const QSet< int > &buttons)
QList< QAbstractButton * > viableButtons() const
void setMinimumRequiredChecked(int checkedBtns)
QList< QAbstractButton * > checkedViableButtons() const
void checkSlideOverNeighborButtons(QMouseEvent *mouseEvent, class QAbstractButton *startingButton)
bool eventFilter(QObject *obj, QEvent *event) override
KisColorLabelMouseDragFilter(QObject *parent=nullptr)
#define bounds(x, a, b)
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
QString button(const QWheelEvent &ev)
QString buttons(const T &ev)
auto filterContainer(C &container, KeepIfFunction keepIf) -> decltype(bool(keepIf(container[0])), void())
KisColorLabelButton::SelectionIndicationType selectionVis
Private(QColor color, uint sizeSquared)