Krita Source Code Documentation
Loading...
Searching...
No Matches
KisSegmentGradientSlider.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2004 Sven Langkamp <sven.langkamp@gmail.com>
4 * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <QPainter>
10#include <QContextMenuEvent>
11#include <QPixmap>
12#include <QMouseEvent>
13#include <QPolygon>
14#include <QPaintEvent>
15#include <QStyleOptionToolButton>
16#include <QWindow>
17#include <QAction>
18#include <QColorDialog>
19
20#include <kis_debug.h>
21#include <klocalizedstring.h>
25#include <krita_utils.h>
26#include <kconfiggroup.h>
27#include <ksharedconfig.h>
28
30
31#define MARGIN 5
32#define HANDLE_SIZE 10
33
34KisSegmentGradientSlider::KisSegmentGradientSlider(QWidget *parent, const char* name, Qt::WindowFlags f)
35 : QWidget(parent, f)
36 , m_drag(false)
37 , m_updateCompressor(40, KisSignalCompressor::FIRST_ACTIVE)
38{
39 setObjectName(name);
40 setMouseTracking(true);
41 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
42 setFocusPolicy(Qt::WheelFocus);
43
44 connect(this, SIGNAL(updateRequested()), &m_updateCompressor, SLOT(start()));
45 connect(&m_updateCompressor, SIGNAL(timeout()), this, SLOT(update()));
46
47 QWindow *window = this->window()->windowHandle();
48 if (window) {
49 connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize()));
50 }
52}
53
61
63{
64 QPainter painter(this);
65
66 const QRect previewRect = gradientStripeRect();
67
68 if (m_gradient) {
69 // Gradient
71 // Handles
72 QList<KoGradientSegment*> segments = m_gradient->segments();
73 painter.setRenderHint(QPainter::Antialiasing, true);
74 const QRect handlesRect = this->handlesStripeRect();
75 const bool hasFocus = this->hasFocus();
76 // Segment handles
78 const KoGradientSegment *selectedSegment = segments[m_selectedHandle.index];
79 QRectF segmentHandleRect =
80 handlesRect.adjusted(
81 selectedSegment->startOffset() * handlesRect.width(),
82 -1,
83 -(handlesRect.width() - selectedSegment->endOffset() * handlesRect.width()),
84 -4
85 );
86 painter.fillRect(segmentHandleRect, palette().highlight());
87 }
90 const KoGradientSegment *hoveredSegment = segments[m_hoveredHandle.index];
91 QRectF segmentHandleRect =
92 handlesRect.adjusted(
93 hoveredSegment->startOffset() * handlesRect.width(),
94 -1,
95 -(handlesRect.width() - hoveredSegment->endOffset() * handlesRect.width()),
96 -4
97 );
98 QColor c = palette().highlight().color();
99 c.setAlpha(96);
100 painter.fillRect(segmentHandleRect, c);
101 }
102 // Mid-Point handles
103 const qreal midPointHandleSize = m_handleSize.height() * 0.5;
104 const qreal midPointHandleOffsetY = (handlesRect.height() - 5.0 - midPointHandleSize) * 0.5;
105 for (int i = 0; i < segments.count(); i++) {
107 // If this handle is selected then we will paint it later
108 // on top of everything else
109 continue;
110 }
111 QPointF handlePos =
112 handlesRect.topLeft() +
113 QPointF(segments[i]->middleOffset() * handlesRect.width(), midPointHandleOffsetY);
115 painter, handlePos, midPointHandleSize,
117 palette().windowText().color(), palette().window().color(), palette().highlight().color()
118 );
119 }
120 // Stop handles
121 const QColor highlightColor = palette().color(QPalette::Highlight);
122 // First stop if it is not selected, in which case it will be painted
123 // later on top of everything else
125 KoGradientSegment* segment = segments.front();
127 // Pass the color info as color 2 so that the type indicator is
128 // shown on the right side of the handle for this stop
130 painter,
131 QPointF(handlesRect.left() + segment->startOffset() * handlesRect.width(), handlesRect.top()),
132 QSizeF(m_handleSize),
133 false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == 0, hasFocus,
134 highlightColor,
135 {},
136 { colorType, segment->startColor().toQColor() }
137 );
138 }
139 // Middle stops
140 if (segments.size() > 1) {
141 for (int i = 0; i < segments.count() - 1; ++i) {
143 // If this handle is selected then we will paint it later
144 // on top of everything else
145 continue;
146 }
147 KoGradientSegment* currentSegment = segments[i];
148 KoGradientSegment* nextSegment = segments[i + 1];
149 // If the end point of the current segment and the start point
150 // of the next segment have the same offset, that means the
151 // segments touch each other, are connected (normal behavior),
152 // so we paint a single special handle.
153 // If the end points have different offsets then krita stills
154 // considers the gradient valid (although Gimp doesn't) so we
155 // paint two different handles.
156 if (currentSegment->endOffset() == nextSegment->startOffset()) {
158 painter,
159 QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
160 QSizeF(m_handleSize),
161 false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
162 highlightColor,
163 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() },
164 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
165 );
166 } else {
168 painter,
169 QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
170 QSizeF(m_handleSize),
171 false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
172 highlightColor,
173 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() }
174 );
176 painter,
177 QPointF(handlesRect.left() + nextSegment->startOffset() * handlesRect.width(), handlesRect.top()),
178 QSizeF(m_handleSize),
179 false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
180 highlightColor,
181 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
182 );
183 }
184 }
185 }
186 // Last stop. This is the last thing to be painted before the selected
187 // handle, so we don't need to make a special case here
188 {
189 KoGradientSegment* segment = segments.back();
192 painter,
193 QPointF(handlesRect.left() + segment->endOffset() * handlesRect.width(), handlesRect.top()),
194 QSizeF(m_handleSize),
196 m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == segments.size() &&
198 hasFocus,
199 highlightColor,
200 { colorType, segment->endColor().toQColor() }
201 );
202 }
203 // Selected stop
205 QPointF handlePos =
206 handlesRect.topLeft() +
207 QPointF(segments[m_selectedHandle.index]->middleOffset() * handlesRect.width(), midPointHandleOffsetY);
209 painter, handlePos, midPointHandleSize,
210 true, false, hasFocus,
211 palette().windowText().color(), palette().window().color(), palette().highlight().color()
212 );
213 } else if (m_selectedHandle.type == HandleType_Stop) {
214 if (m_selectedHandle.index == 0) {
215 KoGradientSegment* segment = segments.front();
217 // Pass the color info as color 2 so that the type indicator is
218 // shown on the right side of the handle for this stop
220 painter,
221 QPointF(handlesRect.left() + segment->startOffset() * handlesRect.width(), handlesRect.top()),
222 QSizeF(m_handleSize),
223 true, false, hasFocus,
224 highlightColor,
225 {},
226 { colorType, segment->startColor().toQColor() }
227 );
228 } else if (m_selectedHandle.index < segments.size()) {
229 KoGradientSegment* currentSegment = segments[m_selectedHandle.index - 1];
230 KoGradientSegment* nextSegment = segments[m_selectedHandle.index];
231 if (currentSegment->endOffset() == nextSegment->startOffset()) {
233 painter,
234 QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
235 QSizeF(m_handleSize),
236 true, false, hasFocus,
237 highlightColor,
238 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() },
240 );
241 } else {
243 painter,
244 QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
245 QSizeF(m_handleSize),
246 true, false, hasFocus,
247 highlightColor,
248 { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() }
249 );
251 painter,
252 QPointF(handlesRect.left() + nextSegment->startOffset() * handlesRect.width(), handlesRect.top()),
253 QSizeF(m_handleSize),
254 true, false, hasFocus,
255 highlightColor,
257 );
258 }
259 }
260 }
261 } else {
262 painter.setPen(palette().color(QPalette::Mid));
263 painter.drawRect(previewRect);
264 }
265}
266
268{
269 if (e->button() != Qt::LeftButton) {
270 QWidget::mousePressEvent(e);
271 return;
272 }
273
274 const QRect rect = sliderRect();
275 const QRect handlesRect = handlesStripeRect();
276
277 // Find segment under cursor
279 const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
280 const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
281 m_dragT = t;
282
283 for (int i = 0; i < m_gradient->segments().size(); ++i) {
284 KoGradientSegment *segment = m_gradient->segments()[i];
285 // Check if a knob was pressed
286 if (qAbs(t - segment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
287 // Left knob was pressed
290 m_drag = true;
291 break;
292 } else if (qAbs(t - segment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
293 // Right knob was pressed
295 selectedHandle.index = i + 1;
296 m_drag = true;
297 break;
298 } else if (qAbs(t - segment->middleOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
299 // middle knob was pressed
302 m_drag = true;
303 break;
304 } else if (t >= segment->startOffset() && t <= segment->endOffset()) {
305 // the segment area was pressed
308 if (e->modifiers() & Qt::ControlModifier) {
309 KoColor color;
310 m_gradient->colorAt(color, t);
312 segment->setMiddleOffset(t);
313 m_gradient->splitSegment(segment);
315 m_selectedHandle.index = i + 1;
316 m_gradient->segments()[i]->setEndColor(color);
317 m_gradient->segments()[i + 1]->setStartColor(color);
318 m_drag = true;
319 Q_EMIT selectedHandleChanged();
320 Q_EMIT updateRequested();
321 return;
322 } else if (e->modifiers() & Qt::ShiftModifier) {
325 return;
326 }
327 m_drag = true;
328 m_relativeDragOffset = t - segment->startOffset();
329 break;
330 }
331 }
332
333 if (m_drag) {
334 m_hoveredHandle = {};
335 }
336
339 Q_EMIT selectedHandleChanged();
340 Q_EMIT updateRequested();
341 }
342}
343
345{
346 Q_UNUSED(e);
348 m_drag = false;
349}
350
352{
353 const QRect rect = sliderRect();
354 const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
355
356 if (m_drag) {
357 if (!(e->buttons() & Qt::LeftButton)) {
358 QWidget::mouseMoveEvent(e);
359 return;
360 }
361 const QRect augmentedRect = kisGrowRect(this->rect(), removeStopDistance);
363 if (augmentedRect.contains(e->pos())) {
364 m_gradient->duplicateSegment(m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1]);
378 }
380 if (augmentedRect.contains(e->pos())) {
381 m_gradient->duplicateSegment(m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1]);
382 KoGradientSegment *previousSegment = m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1];
388 previousSegment->setMiddleOffset(
389 previousSegment->startOffset() +
392 previousSegment->length()
393 );
398 nextSegment->setMiddleOffset(
399 nextSegment->startOffset() +
402 nextSegment->length()
403 );
407 }
408 }
409
412 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
413 if (m_gradient->segments().size() > 1 && m_selectedHandle.index > 0 && m_selectedHandle.index < m_gradient->segments().size() - 1 &&
414 !augmentedRect.contains(e->pos())) {
426 m_gradient->collapseSegment(m_gradient->segments()[m_selectedHandle.index]);
428 } else {
429 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
430 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
431 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[m_selectedHandle.index + 1];
432 if (previousSegment && nextSegment) {
433 const qreal midPointRelativePos = segment->middleOffset() - segment->startOffset();
434 const qreal previousMidPointLocalPos =
435 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
436 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
437 : 0.0;
438 const qreal nextMidPointLocalPos =
439 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
440 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
441 : 0.0;
442 qreal newStartOffset, newEndOffset;
443 if (t < m_dragT) {
444 newStartOffset = qMax(t - m_relativeDragOffset, previousSegment->startOffset() + shrinkEpsilon);
445 newEndOffset = newStartOffset + segment->length();
446 } else {
447 newEndOffset = qMin(t + (segment->length() - m_relativeDragOffset), nextSegment->endOffset() - shrinkEpsilon);
448 newStartOffset = newEndOffset - segment->length();
449 }
450 previousSegment->setEndOffset(newStartOffset);
451 segment->setStartOffset(newStartOffset);
452 segment->setEndOffset(newEndOffset);
453 nextSegment->setStartOffset(newEndOffset);
454 previousSegment->setMiddleOffset(
455 previousSegment->startOffset() +
456 previousMidPointLocalPos * previousSegment->length()
457 );
458 nextSegment->setMiddleOffset(
459 nextSegment->startOffset() +
460 nextMidPointLocalPos * nextSegment->length()
461 );
462 segment->setMiddleOffset(segment->startOffset() + midPointRelativePos);
463 } else {
464 if (!previousSegment) {
465 segment->setStartOffset(0.0);
466 }
467 if (!nextSegment) {
468 segment->setEndOffset(1.0);
469 }
470 }
471 }
472 Q_EMIT selectedHandleChanged();
473 Q_EMIT updateRequested();
474 }
475
476 } else if (m_selectedHandle.type == HandleType_Stop) {
478 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
479 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
480 if (m_gradient->segments().size() > 1 && m_selectedHandle.index > 0 && m_selectedHandle.index < m_gradient->segments().size() &&
481 !augmentedRect.contains(e->pos())) {
496 previousSegment->setEndType(nextSegment->endType());
497 previousSegment->setEndColor(nextSegment->endColor());
500 } else {
501 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
502 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
503 if (previousSegment && nextSegment) {
504 const qreal previousMidPointLocalPos =
505 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
506 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
507 : 0.0;
508 const qreal nextMidPointLocalPos =
509 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
510 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
511 : 0.0;
512 qreal newOffset;
513 if (t < m_dragT) {
514 newOffset = qMax(t, previousSegment->startOffset() + shrinkEpsilon);
515 } else {
516 newOffset = qMin(t, nextSegment->endOffset() - shrinkEpsilon);
517 }
518 previousSegment->setEndOffset(newOffset);
519 nextSegment->setStartOffset(newOffset);
520 previousSegment->setMiddleOffset(
521 previousSegment->startOffset() +
522 previousMidPointLocalPos * previousSegment->length()
523 );
524 nextSegment->setMiddleOffset(
525 nextSegment->startOffset() +
526 nextMidPointLocalPos * nextSegment->length()
527 );
528 } else {
529 if (!previousSegment) {
530 nextSegment->setStartOffset(0.0);
531 }
532 if (!nextSegment) {
533 previousSegment->setEndOffset(1.0);
534 }
535 }
536 }
537 Q_EMIT selectedHandleChanged();
538 Q_EMIT updateRequested();
539 }
540
542 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
543 segment->setMiddleOffset(qBound(segment->startOffset(), t, segment->endOffset()));
544 Q_EMIT selectedHandleChanged();
545 Q_EMIT updateRequested();
546 }
547
548 } else {
549 const QRect handlesRect = handlesStripeRect();
550 Handle hoveredHandle;
551 const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
552 for (int i = 0; i < m_gradient->segments().size(); ++i) {
553 KoGradientSegment *segment = m_gradient->segments()[i];
554 // Check if a knob was hovered
555 if (qAbs(t - segment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
556 // Left knob was hovered
557 hoveredHandle.type = HandleType_Stop;
558 hoveredHandle.index = i;
559 break;
560 } else if (qAbs(t - segment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
561 // Right knob was hovered
562 hoveredHandle.type = HandleType_Stop;
563 hoveredHandle.index = i + 1;
564 break;
565 } else if (qAbs(t - segment->middleOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
566 // middle knob was hovered
567 hoveredHandle.type = HandleType_MidPoint;
568 hoveredHandle.index = i;
569 break;
570 } else if (t >= segment->startOffset() && t <= segment->endOffset()) {
571 // the segment area was hovered
572 hoveredHandle.type = HandleType_Segment;
573 hoveredHandle.index = i;
574 break;
575 }
576 }
577 m_hoveredHandle = hoveredHandle;
578 Q_EMIT updateRequested();
579 }
580}
581
583{
584 if (e->button() != Qt::LeftButton) {
585 QWidget::mouseDoubleClickEvent(e);
586 return;
587 }
588
589 const QRect rect = sliderRect();
590 const QRect handlesRect = handlesStripeRect();
591 const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
592 const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
593 const KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
594 const KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
595
596 if ((previousSegment && qAbs(t - previousSegment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) ||
597 (nextSegment && qAbs(t - nextSegment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y())) {
599 }
600}
601
621
641
642void KisSegmentGradientSlider::handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers)
643{
644 if (direction == 0) {
645 return;
646 }
647 if (modifiers & Qt::ControlModifier) {
648 if (direction < 0) {
650 } else {
652 }
653 } else {
654 const qreal increment = modifiers & Qt::ShiftModifier ? 0.001 : 0.01;
655 moveSelectedHandle(direction < 0 ? -increment : increment);
656 }
657}
658
660{
661 if (e->angleDelta().y() != 0) {
662 handleIncrementInput(e->angleDelta().y(), e->modifiers());
663 e->accept();
664 } else {
665 QWidget::wheelEvent(e);
666 }
667}
668
670{
671 switch (e->key()) {
672 case Qt::Key_Left:
673 handleIncrementInput(-1, e->modifiers());
674 break;
675 case Qt::Key_Right:
676 handleIncrementInput(1, e->modifiers());
677 break;
678 case Qt::Key_Return:
679 case Qt::Key_Enter:
681 break;
682 case Qt::Key_Delete:
684 break;
685 default:
686 QWidget::keyPressEvent(e);
687 break;
688 }
689}
690
692{
693 m_hoveredHandle = {};
694 Q_EMIT updateRequested();
695 QWidget::leaveEvent(e);
696}
697
698void KisSegmentGradientSlider::moveHandle(Handle handle, qreal distance, bool useShrinkEpsilon)
699{
700 const qreal epsilon = useShrinkEpsilon ? shrinkEpsilon : 0.0;
701 if (handle.type == HandleType_Segment) {
702 KoGradientSegment *segment = m_gradient->segments()[handle.index];
703 KoGradientSegment *previousSegment = handle.index == 0 ? nullptr : m_gradient->segments()[handle.index - 1];
704 KoGradientSegment *nextSegment = handle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[handle.index + 1];
705 if (previousSegment && nextSegment) {
706 const qreal midPointRelativePos = segment->middleOffset() - segment->startOffset();
707 const qreal previousMidPointLocalPos =
708 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
709 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
710 : 0.0;
711 const qreal nextMidPointLocalPos =
712 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
713 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
714 : 0.0;
715 qreal newStartOffset, newEndOffset;
716 if (distance < 0.0) {
717 newStartOffset = qMax(segment->startOffset() + distance, previousSegment->startOffset() + epsilon);
718 newEndOffset = newStartOffset + segment->length();
719 } else {
720 newEndOffset = qMin(segment->endOffset() + distance, nextSegment->endOffset() - epsilon);
721 newStartOffset = newEndOffset - segment->length();
722 }
723 previousSegment->setEndOffset(newStartOffset);
724 segment->setStartOffset(newStartOffset);
725 segment->setEndOffset(newEndOffset);
726 nextSegment->setStartOffset(newEndOffset);
727 previousSegment->setMiddleOffset(
728 previousSegment->startOffset() +
729 previousMidPointLocalPos * previousSegment->length()
730 );
731 nextSegment->setMiddleOffset(
732 nextSegment->startOffset() +
733 nextMidPointLocalPos * nextSegment->length()
734 );
735 segment->setMiddleOffset(segment->startOffset() + midPointRelativePos);
736 } else {
737 if (!previousSegment) {
738 segment->setStartOffset(0.0);
739 }
740 if (!nextSegment) {
741 segment->setEndOffset(1.0);
742 }
743 }
744 Q_EMIT selectedHandleChanged();
745 Q_EMIT updateRequested();
746 } else if (handle.type == HandleType_Stop) {
747 KoGradientSegment *previousSegment = handle.index == 0 ? nullptr : m_gradient->segments()[handle.index - 1];
748 KoGradientSegment *nextSegment = handle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[handle.index];
749 if (previousSegment && nextSegment) {
750 const qreal previousMidPointLocalPos =
751 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
752 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
753 : 0.0;
754 const qreal nextMidPointLocalPos =
755 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
756 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
757 : 0.0;
758 qreal newOffset;
759 if (distance < 0) {
760 newOffset = qMax(previousSegment->endOffset() + distance, previousSegment->startOffset() + epsilon);
761 } else {
762 newOffset = qMin(previousSegment->endOffset() + distance, nextSegment->endOffset() - epsilon);
763 }
764 previousSegment->setEndOffset(newOffset);
765 nextSegment->setStartOffset(newOffset);
766 previousSegment->setMiddleOffset(
767 previousSegment->startOffset() +
768 previousMidPointLocalPos * previousSegment->length()
769 );
770 nextSegment->setMiddleOffset(
771 nextSegment->startOffset() +
772 nextMidPointLocalPos * nextSegment->length()
773 );
774 } else {
775 if (!previousSegment) {
776 nextSegment->setStartOffset(0.0);
777 }
778 if (!nextSegment) {
779 previousSegment->setEndOffset(1.0);
780 }
781 }
782 Q_EMIT selectedHandleChanged();
783 Q_EMIT updateRequested();
784 } else if (handle.type == HandleType_MidPoint) {
785 KoGradientSegment *segment = m_gradient->segments()[handle.index];
786 segment->setMiddleOffset(qBound(segment->startOffset(), segment->middleOffset() + distance, segment->endOffset()));
787 Q_EMIT selectedHandleChanged();
788 Q_EMIT updateRequested();
789 }
790}
791
792void KisSegmentGradientSlider::moveHandleLeft(Handle handle, qreal distance, bool useShrinkEpsilon)
793{
794 moveHandle(handle, -distance, useShrinkEpsilon);
795}
796
797void KisSegmentGradientSlider::moveHandleRight(Handle handle, qreal distance, bool useShrinkEpsilon)
798{
799 moveHandle(handle, distance, useShrinkEpsilon);
800}
801
803{
804 moveHandle(m_selectedHandle, distance, useShrinkEpsilon);
805}
806
808{
809 moveSelectedHandle(-distance, useShrinkEpsilon);
810}
811
813{
814 moveSelectedHandle(distance, useShrinkEpsilon);
815}
816
818{
819 if (handle.type == HandleType_Segment) {
820 if (m_gradient->removeSegment(m_gradient->segments()[handle.index])) {
821 if (m_selectedHandle.index > 0) {
823 }
824 return true;
825 }
826 } else if (m_selectedHandle.type == HandleType_Stop) {
827 if (m_selectedHandle.index <= 0 || m_selectedHandle.index >= m_gradient->segments().size()) {
828 return false;
829 }
830 KoGradientSegment *previousSegment = m_gradient->segments()[m_selectedHandle.index - 1];
831 KoGradientSegment *nextSegment = m_gradient->segments()[m_selectedHandle.index];
832 const qreal middleOffset = previousSegment->endOffset();
833 previousSegment->setEndType(nextSegment->endType());
834 previousSegment->setEndColor(nextSegment->endColor());
835 m_gradient->removeSegment(nextSegment);
836 previousSegment->setMiddleOffset(middleOffset);
839 return true;
840 }
841 return false;
842}
843
845{
846 if (deleteHandleImpl(handle)) {
847 Q_EMIT selectedHandleChanged();
848 Q_EMIT updateRequested();
849 }
850}
851
856
858{
860 return;
861 }
862 if (m_gradient->collapseSegment(m_gradient->segments()[m_selectedHandle.index])) {
863 if (m_selectedHandle.index == m_gradient->segments().size()) {
865 }
866 Q_EMIT selectedHandleChanged();
867 Q_EMIT updateRequested();
868 }
869}
870
872{
874 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
875 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
876 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[m_selectedHandle.index + 1];
877 if (previousSegment && nextSegment) {
879 (previousSegment->startOffset() + nextSegment->endOffset()) / 2.0 -
880 (segment->startOffset() + segment->endOffset()) / 2.0);
881 }
882 } else if (m_selectedHandle.type == HandleType_Stop) {
883 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
884 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
885 if (previousSegment && nextSegment) {
886 moveSelectedHandle((previousSegment->startOffset() + nextSegment->endOffset()) / 2.0 - nextSegment->startOffset());
887 }
889 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
890 qDebug() << segment->startOffset() << segment->endOffset() << segment->middleOffset() <<
891 ((segment->startOffset() + segment->endOffset()) / 2);
892 moveSelectedHandle((segment->startOffset() + segment->endOffset()) / 2.0 - segment->middleOffset());
893 }
894}
895
897{
899 return;
900 }
901 m_gradient->splitSegment(m_gradient->segments()[m_selectedHandle.index]);
902 Q_EMIT selectedHandleChanged();
903 Q_EMIT updateRequested();
904}
905
907{
909 return;
910 }
911 m_gradient->duplicateSegment(m_gradient->segments()[m_selectedHandle.index]);
912 Q_EMIT selectedHandleChanged();
913 Q_EMIT updateRequested();
914}
915
917{
919 return;
920 }
921 m_gradient->mirrorSegment(m_gradient->segments()[m_selectedHandle.index]);
922 Q_EMIT selectedHandleChanged();
923 Q_EMIT updateRequested();
924}
925
927{
928 QList<KoGradientSegment*> oldSegments = m_gradient->segments();
929 QList<KoGradientSegment*> newSegments;
930 for (int i = oldSegments.size() - 1; i >= 0; --i) {
931 KoGradientSegment* oldSegment = oldSegments[i];
932 int interpolation = oldSegment->interpolation();
933 int colorInterpolation = oldSegment->colorInterpolation();
934
935 if (interpolation == INTERP_SPHERE_INCREASING) {
936 interpolation = INTERP_SPHERE_DECREASING;
937 }
938 else if (interpolation == INTERP_SPHERE_DECREASING) {
939 interpolation = INTERP_SPHERE_INCREASING;
940 }
941 if (colorInterpolation == COLOR_INTERP_HSV_CW) {
942 colorInterpolation = COLOR_INTERP_HSV_CCW;
943 }
944 else if (colorInterpolation == COLOR_INTERP_HSV_CCW) {
945 colorInterpolation = COLOR_INTERP_HSV_CW;
946 }
947
948 KoGradientSegment* newSegment = new KoGradientSegment(
949 interpolation, colorInterpolation,
950 { 1.0 - oldSegment->endOffset(), oldSegment->endColor(), oldSegment->endType() },
951 { 1.0 - oldSegment->startOffset(), oldSegment->startColor(), oldSegment->startType() },
952 1.0 - oldSegment->middleOffset()
953 );
954
955 newSegments.push_back(newSegment);
956 }
957 m_gradient->setSegments(newSegments);
959 m_selectedHandle.index = newSegments.size() - m_selectedHandle.index;
960 } else {
961 m_selectedHandle.index = newSegments.size() - 1 - m_selectedHandle.index;
962 }
963 Q_EMIT selectedHandleChanged();
964 Q_EMIT updateRequested();
965}
966
968{
969 const qreal size = 1.0 / m_gradient->segments().size();
970 for (int i = 0; i < m_gradient->segments().size(); ++i) {
971 KoGradientSegment *segment = m_gradient->segments()[i];
972 const qreal relativeMidPointPosition =
973 (segment->middleOffset() - segment->startOffset()) /
974 (segment->endOffset() - segment->startOffset());
975 segment->setStartOffset(i * size);
976 segment->setEndOffset((i + 1) * size);
977 segment->setMiddleOffset(
978 segment->startOffset() + relativeMidPointPosition *
979 (segment->endOffset() - segment->startOffset()));
980 }
981 Q_EMIT selectedHandleChanged();
982 Q_EMIT updateRequested();
983}
984
986{
987 const qreal handleWidthOverTwo = static_cast<qreal>(m_handleSize.width()) / 2.0;
988 const int hMargin = static_cast<int>(std::ceil(handleWidthOverTwo)) + 2;
989 return rect().adjusted(hMargin, 0, -hMargin, 0);
990}
991
993{
994 const QRect rc = sliderRect();
995 return rc.adjusted(0, 0, 0, -m_handleSize.height() - 4);
996}
997
999{
1000 const QRect rc = sliderRect();
1001 return rc.adjusted(0, rc.height() - (m_handleSize.height() + 2), 0, -2);
1002}
1003
1005{
1006 QFontMetrics fm(font());
1007 const int h = qMax(15, static_cast<int>(std::ceil(fm.height() * 0.75)));
1008 m_handleSize = QSize(h * 0.75, h);
1009}
1010
1012{
1013 QFontMetrics fm(font());
1014 const int h = fm.height();
1015
1016 QStyleOptionToolButton opt;
1017 QSize sz = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this);
1018
1019 return qMax(32, sz.height()) + m_handleSize.height();
1020}
1021
1023{
1024 const int h = minimalHeight();
1025 return QSize(2 * h, h);
1026}
1027
1029{
1030 const int h = minimalHeight();
1031 return QSize(h, h);
1032}
1033
1035{
1037 return;
1038 }
1039 QList<KoGradientSegment*> segments = m_gradient->segments();
1040 if (m_selectedHandle.index < 0 || m_selectedHandle.index > segments.size()) {
1041 return;
1042 }
1043
1044 KoColor color1, color2;
1046 if (m_selectedHandle.index == 0) {
1047 endType1 = segments[0]->startType();
1048 color1 = segments[0]->startColor();
1049 } else {
1050 endType1 = segments[m_selectedHandle.index - 1]->endType();
1051 color1 = segments[m_selectedHandle.index - 1]->endColor();
1052 if (m_selectedHandle.index < segments.size()) {
1053 endType2 = segments[m_selectedHandle.index]->startType();
1054 color2 = segments[m_selectedHandle.index]->startColor();
1055 }
1056 }
1057
1058 KConfigGroup cfg = KSharedConfig::openConfig()->group("colorselector");
1059 bool usePlatformDialog = cfg.readEntry("UsePlatformColorDialog", false);
1060 QDialog *colorDialog = nullptr;
1061
1062 if (!usePlatformDialog) {
1064 KisDlgInternalColorSelector *dialog = new KisDlgInternalColorSelector(this, color1, cfg, i18n("Choose a color"));
1065 dialog->setPreviousColor(color1);
1066 auto setColorFn = [dialog, segments, this]() mutable
1067 {
1068 if (m_selectedHandle.index == 0) {
1069 segments[0]->setStartType(COLOR_ENDPOINT);
1070 segments[0]->setStartColor(dialog->getCurrentColor());
1071 } else {
1072 segments[m_selectedHandle.index - 1]->setEndType(COLOR_ENDPOINT);
1073 segments[m_selectedHandle.index - 1]->setEndColor(dialog->getCurrentColor());
1074 if (m_selectedHandle.index < segments.size()) {
1075 segments[m_selectedHandle.index]->setStartType(COLOR_ENDPOINT);
1076 segments[m_selectedHandle.index]->setStartColor(dialog->getCurrentColor());
1077 }
1078 }
1079 Q_EMIT selectedHandleChanged();
1080 Q_EMIT updateRequested();
1081 };
1083 connect(dialog, &QDialog::accepted, setColorFn);
1084 colorDialog = dialog;
1085 } else {
1086 QColorDialog *dialog = new QColorDialog(this);
1087 dialog->setCurrentColor(color1.toQColor());
1088 auto setColorFn = [dialog, segments, this]() mutable
1089 {
1090 KoColor color;
1091 color.fromQColor(dialog->currentColor());
1092 if (m_selectedHandle.index == 0) {
1093 segments[0]->setStartType(COLOR_ENDPOINT);
1094 segments[0]->setStartColor(color);
1095 } else {
1096 segments[m_selectedHandle.index - 1]->setEndType(COLOR_ENDPOINT);
1097 segments[m_selectedHandle.index - 1]->setEndColor(color);
1098 if (m_selectedHandle.index < segments.size()) {
1099 segments[m_selectedHandle.index]->setStartType(COLOR_ENDPOINT);
1100 segments[m_selectedHandle.index]->setStartColor(color);
1101 }
1102 }
1103 Q_EMIT selectedHandleChanged();
1104 Q_EMIT updateRequested();
1105 };
1106 connect(dialog, &QColorDialog::currentColorChanged, setColorFn);
1107 connect(dialog, &QDialog::accepted, setColorFn);
1108 colorDialog = dialog;
1109 }
1110 // common functionality
1111 connect(colorDialog, &QDialog::rejected, [endType1, endType2, color1, color2, segments, this]()
1112 {
1113 if (m_selectedHandle.index == 0) {
1114 segments[0]->setStartType(endType1);
1115 segments[0]->setStartColor(color1);
1116 } else {
1117 segments[m_selectedHandle.index - 1]->setEndType(endType1);
1118 segments[m_selectedHandle.index - 1]->setEndColor(color1);
1119 if (m_selectedHandle.index < segments.size()) {
1120 segments[m_selectedHandle.index]->setStartType(endType2);
1121 segments[m_selectedHandle.index]->setStartColor(color2);
1122 }
1123 }
1124 Q_EMIT selectedHandleChanged();
1125 Q_EMIT updateRequested();
1126 });
1127 colorDialog->setAttribute(Qt::WA_DeleteOnClose);
1128 colorDialog->show();
1129 colorDialog->raise();
1130 colorDialog->activateWindow();
1131}
qreal distance(const QPointF &p1, const QPointF &p2)
KoGradientSegmentEndpointType
@ COLOR_ENDPOINT
@ INTERP_SPHERE_DECREASING
@ INTERP_SPHERE_INCREASING
@ COLOR_INTERP_HSV_CCW
@ COLOR_INTERP_HSV_CW
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
The KisInternalColorSelector class.
void signalForegroundColorChosen(KoColor color)
signalForegroundColorChosen The most important signal. This will sent out when a color has been chose...
void moveSelectedHandle(qreal distance, bool useShrinkEpsilon=true)
void leaveEvent(QEvent *e) override
void moveHandleLeft(Handle handle, qreal distance, bool useShrinkEpsilon=true)
void moveSelectedHandleRight(qreal distance, bool useShrinkEpsilon=true)
void mouseDoubleClickEvent(QMouseEvent *e) override
void mousePressEvent(QMouseEvent *e) override
static constexpr int removeStopDistance
static constexpr qreal shrinkEpsilon
KisSegmentGradientSlider(QWidget *parent=nullptr, const char *name=nullptr, Qt::WindowFlags f=Qt::WindowFlags())
TemporallyDeletedHandleInfo m_temporallyDeletedHandleInfo
void mouseReleaseEvent(QMouseEvent *e) override
void handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers)
void setGradientResource(KoSegmentGradientSP agr)
void paintEvent(QPaintEvent *) override
void keyPressEvent(QKeyEvent *e) override
void wheelEvent(QWheelEvent *e) override
void moveHandle(Handle handle, qreal distance, bool useShrinkEpsilon=true)
void moveHandleRight(Handle handle, qreal distance, bool useShrinkEpsilon=true)
void moveSelectedHandleLeft(qreal distance, bool useShrinkEpsilon=true)
void mouseMoveEvent(QMouseEvent *e) override
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
Write API docs here.
void setEndType(KoGradientSegmentEndpointType type)
void setInterpolation(int interpolationType)
void setMiddleOffset(qreal t)
KoGradientSegmentEndpointType endType() const
void setColorInterpolation(int colorInterpolationType)
KoGradientSegmentEndpointType startType() const
void setStartColor(const KoColor &color)
void setStartType(KoGradientSegmentEndpointType type)
void setStartOffset(qreal t)
void setEndColor(const KoColor &color)
const KoColor & startColor() const
const KoColor & endColor() const
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
void paintGradientBox(QPainter &painter, const KoAbstractGradientSP gradient, const QRectF &rect)
void paintStopHandle(QPainter &painter, const QPointF &position, const QSizeF &size, bool isSelected, bool isHovered, bool hasFocus, const QColor &highlightColor, const StopHandleColor &color1, const StopHandleColor &color2)
KisGradientWidgetsUtils::ColorType segmentEndPointTypeToColorType(KoGradientSegmentEndpointType type)
void paintMidPointHandle(QPainter &painter, const QPointF &position, qreal size, bool isSelected, bool isHovered, bool hasFocus, const QColor &borderColor, const QColor &fillColor, const QColor &highlightColor)
rgba palette[MAX_PALETTE]
Definition palette.c:35