File indexing completed on 2024-12-22 04:13:07

0001 /*
0002  *  SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net>
0003  *  SPDX-FileCopyrightText: 2004 Sven Langkamp <sven.langkamp@gmail.com>
0004  *  SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <QPainter>
0010 #include <QContextMenuEvent>
0011 #include <QPixmap>
0012 #include <QMouseEvent>
0013 #include <QPolygon>
0014 #include <QPaintEvent>
0015 #include <QMenu>
0016 #include <QStyleOptionToolButton>
0017 #include <QWindow>
0018 #include <QAction>
0019 #include <QColorDialog>
0020 
0021 #include <kis_debug.h>
0022 #include <klocalizedstring.h>
0023 #include <resources/KoSegmentGradient.h>
0024 #include <KisGradientWidgetsUtils.h>
0025 #include <KisDlgInternalColorSelector.h>
0026 #include <krita_utils.h>
0027 #include <kconfiggroup.h>
0028 #include <ksharedconfig.h>
0029 
0030 #include "KisSegmentGradientSlider.h"
0031 
0032 #define MARGIN 5
0033 #define HANDLE_SIZE 10
0034 
0035 KisSegmentGradientSlider::KisSegmentGradientSlider(QWidget *parent, const char* name, Qt::WindowFlags f)
0036     : QWidget(parent, f)
0037     , m_drag(false)
0038     , m_updateCompressor(40, KisSignalCompressor::FIRST_ACTIVE)
0039 {
0040     setObjectName(name);
0041     setMouseTracking(true);
0042     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
0043     setFocusPolicy(Qt::WheelFocus);
0044 
0045     connect(this, SIGNAL(updateRequested()), &m_updateCompressor, SLOT(start()));
0046     connect(&m_updateCompressor, SIGNAL(timeout()), this, SLOT(update()));
0047 
0048     QWindow *window = this->window()->windowHandle();
0049     if (window) {
0050         connect(window, SIGNAL(screenChanged(QScreen*)), SLOT(updateHandleSize()));
0051     }
0052     updateHandleSize();
0053 }
0054 
0055 void KisSegmentGradientSlider::setGradientResource(KoSegmentGradientSP agr)
0056 {
0057     m_gradient = agr;
0058     m_selectedHandle = { HandleType_Stop, 0 };
0059     emit selectedHandleChanged();
0060     emit updateRequested();
0061 }
0062 
0063 void KisSegmentGradientSlider::paintEvent(QPaintEvent*)
0064 {
0065     QPainter painter(this);
0066 
0067     const QRect previewRect = gradientStripeRect();
0068     
0069     if (m_gradient) {
0070         // Gradient
0071         KisGradientWidgetsUtils::paintGradientBox(painter, m_gradient, previewRect);
0072         // Handles
0073         QList<KoGradientSegment*> segments = m_gradient->segments();
0074         painter.setRenderHint(QPainter::Antialiasing, true);
0075         const QRect handlesRect = this->handlesStripeRect();
0076         const bool hasFocus = this->hasFocus();
0077         // Segment handles
0078         if (m_selectedHandle.type == HandleType_Segment) {
0079             const KoGradientSegment *selectedSegment = segments[m_selectedHandle.index];
0080             QRectF segmentHandleRect =
0081                 handlesRect.adjusted(
0082                     selectedSegment->startOffset() * handlesRect.width(),
0083                     -1,
0084                     -(handlesRect.width() - selectedSegment->endOffset() * handlesRect.width()),
0085                     -4
0086                 );
0087             painter.fillRect(segmentHandleRect, palette().highlight());
0088         }
0089         if (m_hoveredHandle.type == HandleType_Segment &&
0090             (m_selectedHandle.type != HandleType_Segment || m_hoveredHandle.index != m_selectedHandle.index)) {
0091             const KoGradientSegment *hoveredSegment = segments[m_hoveredHandle.index];
0092             QRectF segmentHandleRect =
0093                 handlesRect.adjusted(
0094                     hoveredSegment->startOffset() * handlesRect.width(),
0095                     -1,
0096                     -(handlesRect.width() - hoveredSegment->endOffset() * handlesRect.width()),
0097                     -4
0098                 );
0099             QColor c = palette().highlight().color();
0100             c.setAlpha(96);
0101             painter.fillRect(segmentHandleRect, c);
0102         }
0103         // Mid-Point handles
0104         const qreal midPointHandleSize = m_handleSize.height() * 0.5;
0105         const qreal midPointHandleOffsetY = (handlesRect.height() - 5.0 - midPointHandleSize) * 0.5;
0106         for (int i = 0; i < segments.count(); i++) {
0107             if (m_selectedHandle.type == HandleType_MidPoint && m_selectedHandle.index == i) {
0108                 // If this handle is selected then we will paint it later
0109                 // on top of everything else
0110                 continue;
0111             }
0112             QPointF handlePos =
0113                 handlesRect.topLeft() +
0114                 QPointF(segments[i]->middleOffset() * handlesRect.width(), midPointHandleOffsetY);
0115             KisGradientWidgetsUtils::paintMidPointHandle(
0116                 painter, handlePos, midPointHandleSize,
0117                 false, m_hoveredHandle.type == HandleType_MidPoint && m_hoveredHandle.index == i, hasFocus,
0118                 palette().windowText().color(), palette().window().color(), palette().highlight().color()
0119             );
0120         }
0121         // Stop handles
0122         const QColor highlightColor = palette().color(QPalette::Highlight);
0123         // First stop if it is not selected, in which case it will be painted 
0124         // later on top of everything else
0125         if (m_selectedHandle.type != HandleType_Stop || m_selectedHandle.index != 0) {
0126             KoGradientSegment* segment = segments.front();
0127             KisGradientWidgetsUtils::ColorType colorType = KisGradientWidgetsUtils::segmentEndPointTypeToColorType(segment->startType());
0128             // Pass the color info as color 2 so that the type indicator is
0129             // shown on the right side of the handle for this stop
0130             KisGradientWidgetsUtils::paintStopHandle(
0131                 painter,
0132                 QPointF(handlesRect.left() + segment->startOffset() * handlesRect.width(), handlesRect.top()),
0133                 QSizeF(m_handleSize),
0134                 false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == 0, hasFocus,
0135                 highlightColor,
0136                 {},
0137                 { colorType, segment->startColor().toQColor() }
0138             );
0139         }
0140         // Middle stops
0141         if (segments.size() > 1) {
0142             for (int i = 0; i < segments.count() - 1; ++i) {
0143                 if (m_selectedHandle.type == HandleType_Stop && m_selectedHandle.index == i + 1) {
0144                     // If this handle is selected then we will paint it later
0145                     // on top of everything else
0146                     continue;
0147                 }
0148                 KoGradientSegment* currentSegment = segments[i];
0149                 KoGradientSegment* nextSegment = segments[i + 1];
0150                 // If the end point of the current segment and the start point
0151                 // of the next segment have the same offset, that means the
0152                 // segments touch each other, are connected (normal behavior),
0153                 // so we paint a single special handle.
0154                 // If the end points have different offsets then krita stills
0155                 // considers the gradient valid (although Gimp doesn't) so we
0156                 // paint two different handles. 
0157                 if (currentSegment->endOffset() == nextSegment->startOffset()) {
0158                     KisGradientWidgetsUtils::paintStopHandle(
0159                         painter,
0160                         QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
0161                         QSizeF(m_handleSize),
0162                         false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
0163                         highlightColor,
0164                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() },
0165                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
0166                     );
0167                 } else {
0168                     KisGradientWidgetsUtils::paintStopHandle(
0169                         painter,
0170                         QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
0171                         QSizeF(m_handleSize),
0172                         false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
0173                         highlightColor,
0174                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() }
0175                     );
0176                     KisGradientWidgetsUtils::paintStopHandle(
0177                         painter,
0178                         QPointF(handlesRect.left() + nextSegment->startOffset() * handlesRect.width(), handlesRect.top()),
0179                         QSizeF(m_handleSize),
0180                         false, m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == i + 1, hasFocus,
0181                         highlightColor,
0182                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
0183                     );
0184                 }
0185             }
0186         }
0187         // Last stop. This is the last thing to be painted before the selected
0188         // handle, so we don't need to make a special case here
0189         {
0190             KoGradientSegment* segment = segments.back();
0191             KisGradientWidgetsUtils::ColorType colorType = KisGradientWidgetsUtils::segmentEndPointTypeToColorType(segment->endType());
0192             KisGradientWidgetsUtils::paintStopHandle(
0193                 painter,
0194                 QPointF(handlesRect.left() + segment->endOffset() * handlesRect.width(), handlesRect.top()),
0195                 QSizeF(m_handleSize),
0196                 m_selectedHandle.type == HandleType_Stop && m_selectedHandle.index == segments.size(),
0197                 m_hoveredHandle.type == HandleType_Stop && m_hoveredHandle.index == segments.size() &&
0198                 (m_selectedHandle.type != HandleType_Stop || m_selectedHandle.index != segments.size()),
0199                 hasFocus,
0200                 highlightColor,
0201                 { colorType, segment->endColor().toQColor() }
0202             );
0203         }
0204         // Selected stop
0205         if (m_selectedHandle.type == HandleType_MidPoint) {
0206             QPointF handlePos =
0207                 handlesRect.topLeft() +
0208                 QPointF(segments[m_selectedHandle.index]->middleOffset() * handlesRect.width(), midPointHandleOffsetY);
0209             KisGradientWidgetsUtils::paintMidPointHandle(
0210                 painter, handlePos, midPointHandleSize,
0211                 true, false, hasFocus,
0212                 palette().windowText().color(), palette().window().color(), palette().highlight().color()
0213             );
0214         } else if (m_selectedHandle.type == HandleType_Stop) {
0215             if (m_selectedHandle.index == 0) {
0216                 KoGradientSegment* segment = segments.front();
0217                 KisGradientWidgetsUtils::ColorType colorType = KisGradientWidgetsUtils::segmentEndPointTypeToColorType(segment->startType());
0218                 // Pass the color info as color 2 so that the type indicator is
0219                 // shown on the right side of the handle for this stop
0220                 KisGradientWidgetsUtils::paintStopHandle(
0221                     painter,
0222                     QPointF(handlesRect.left() + segment->startOffset() * handlesRect.width(), handlesRect.top()),
0223                     QSizeF(m_handleSize),
0224                     true, false, hasFocus,
0225                     highlightColor,
0226                     {},
0227                     { colorType, segment->startColor().toQColor() }
0228                 );
0229             } else if (m_selectedHandle.index < segments.size()) {
0230                 KoGradientSegment* currentSegment = segments[m_selectedHandle.index - 1];
0231                 KoGradientSegment* nextSegment = segments[m_selectedHandle.index];
0232                 if (currentSegment->endOffset() == nextSegment->startOffset()) {
0233                     KisGradientWidgetsUtils::paintStopHandle(
0234                         painter,
0235                         QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
0236                         QSizeF(m_handleSize),
0237                         true, false, hasFocus,
0238                         highlightColor,
0239                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() },
0240                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
0241                     );
0242                 } else {
0243                     KisGradientWidgetsUtils::paintStopHandle(
0244                         painter,
0245                         QPointF(handlesRect.left() + currentSegment->endOffset() * handlesRect.width(), handlesRect.top()),
0246                         QSizeF(m_handleSize),
0247                         true, false, hasFocus,
0248                         highlightColor,
0249                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(currentSegment->endType()), currentSegment->endColor().toQColor() }
0250                     );
0251                     KisGradientWidgetsUtils::paintStopHandle(
0252                         painter,
0253                         QPointF(handlesRect.left() + nextSegment->startOffset() * handlesRect.width(), handlesRect.top()),
0254                         QSizeF(m_handleSize),
0255                         true, false, hasFocus,
0256                         highlightColor,
0257                         { KisGradientWidgetsUtils::segmentEndPointTypeToColorType(nextSegment->startType()), nextSegment->startColor().toQColor() }
0258                     );
0259                 }
0260             }
0261         }
0262     } else {
0263         painter.setPen(palette().color(QPalette::Mid));
0264         painter.drawRect(previewRect);
0265     }
0266 }
0267 
0268 void KisSegmentGradientSlider::mousePressEvent(QMouseEvent * e)
0269 {
0270     if (e->button() != Qt::LeftButton) {
0271         QWidget::mousePressEvent(e);
0272         return;
0273     }
0274 
0275     const QRect rect = sliderRect();
0276     const QRect handlesRect = handlesStripeRect();
0277 
0278     // Find segment under cursor
0279     Handle selectedHandle;
0280     const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
0281     const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
0282     m_dragT = t;
0283 
0284     for (int i = 0; i < m_gradient->segments().size(); ++i) {
0285         KoGradientSegment *segment = m_gradient->segments()[i];
0286         // Check if a knob was pressed
0287         if (qAbs(t - segment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0288             // Left knob was pressed
0289             selectedHandle.type = HandleType_Stop;
0290             selectedHandle.index = i;
0291             m_drag = true;
0292             break;
0293         } else if (qAbs(t - segment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0294             // Right knob was pressed
0295             selectedHandle.type = HandleType_Stop;
0296             selectedHandle.index = i + 1;
0297             m_drag = true;
0298             break;
0299         } else if (qAbs(t - segment->middleOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0300             // middle knob was pressed
0301             selectedHandle.type = HandleType_MidPoint;
0302             selectedHandle.index = i;
0303             m_drag = true;
0304             break;
0305         } else if (t >= segment->startOffset() && t <= segment->endOffset()) {
0306             // the segment area was pressed
0307             selectedHandle.type = HandleType_Segment;
0308             selectedHandle.index = i;
0309             if (e->modifiers() & Qt::ControlModifier) {
0310                 KoColor color;
0311                 m_gradient->colorAt(color, t);
0312                 m_selectedHandle = selectedHandle;
0313                 segment->setMiddleOffset(t);
0314                 m_gradient->splitSegment(segment);
0315                 m_selectedHandle.type = HandleType_Stop;
0316                 m_selectedHandle.index = i + 1;
0317                 m_gradient->segments()[i]->setEndColor(color);
0318                 m_gradient->segments()[i + 1]->setStartColor(color);
0319                 m_drag = true;
0320                 emit selectedHandleChanged();
0321                 emit updateRequested();
0322                 return;
0323             } else if (e->modifiers() & Qt::ShiftModifier) {
0324                 m_selectedHandle = selectedHandle;
0325                 duplicateSelectedSegment();
0326                 return;
0327             }
0328             m_drag = true;
0329             m_relativeDragOffset = t - segment->startOffset();
0330             break;
0331         }
0332     }
0333 
0334     if (m_drag) {
0335         m_hoveredHandle = {};
0336     }
0337 
0338     if (m_selectedHandle.type != selectedHandle.type || m_selectedHandle.index != selectedHandle.index) {
0339         m_selectedHandle = selectedHandle;
0340         emit selectedHandleChanged();
0341         emit updateRequested();
0342     }
0343 }
0344 
0345 void KisSegmentGradientSlider::mouseReleaseEvent(QMouseEvent * e)
0346 {
0347     Q_UNUSED(e);
0348     m_temporallyDeletedHandleInfo.handle.type = HandleType_None;
0349     m_drag = false;
0350 }
0351 
0352 void KisSegmentGradientSlider::mouseMoveEvent(QMouseEvent * e)
0353 {
0354     const QRect rect = sliderRect();
0355     const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
0356 
0357     if (m_drag) {
0358         if (!(e->buttons() & Qt::LeftButton)) {
0359             QWidget::mouseMoveEvent(e);
0360             return;
0361         }
0362         const QRect augmentedRect = kisGrowRect(this->rect(), removeStopDistance);
0363         if (m_temporallyDeletedHandleInfo.handle.type == HandleType_Segment) {
0364             if (augmentedRect.contains(e->pos())) {
0365                 m_gradient->duplicateSegment(m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1]);
0366                 KoGradientSegment *segment = m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index];
0367                 segment->setStartType(m_temporallyDeletedHandleInfo.leftEndPointType);
0368                 segment->setStartOffset(m_temporallyDeletedHandleInfo.leftEndPointOffset);
0369                 segment->setStartColor(m_temporallyDeletedHandleInfo.leftEndPointColor);
0370                 segment->setEndType(m_temporallyDeletedHandleInfo.rightEndPointType);
0371                 segment->setEndOffset(m_temporallyDeletedHandleInfo.rightEndPointOffset);
0372                 segment->setEndColor(m_temporallyDeletedHandleInfo.rightEndPointColor);
0373                 segment->setMiddleOffset(m_temporallyDeletedHandleInfo.leftMiddleOffset);
0374                 segment->setInterpolation(m_temporallyDeletedHandleInfo.leftInterpolationType);
0375                 segment->setColorInterpolation(m_temporallyDeletedHandleInfo.leftColorInterpolationType);
0376                 m_selectedHandle.type = HandleType_Segment;
0377                 m_selectedHandle.index = m_temporallyDeletedHandleInfo.handle.index;
0378                 m_temporallyDeletedHandleInfo.handle.type = HandleType_None;
0379             }
0380         } else if (m_temporallyDeletedHandleInfo.handle.type == HandleType_Stop) {
0381             if (augmentedRect.contains(e->pos())) {
0382                 m_gradient->duplicateSegment(m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1]);
0383                 KoGradientSegment *previousSegment = m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index - 1];
0384                 KoGradientSegment *nextSegment = m_gradient->segments()[m_temporallyDeletedHandleInfo.handle.index];
0385                 previousSegment->setEndType(m_temporallyDeletedHandleInfo.leftEndPointType);
0386                 previousSegment->setEndColor(m_temporallyDeletedHandleInfo.leftEndPointColor);
0387                 previousSegment->setInterpolation(m_temporallyDeletedHandleInfo.leftInterpolationType);
0388                 previousSegment->setColorInterpolation(m_temporallyDeletedHandleInfo.leftColorInterpolationType);
0389                 previousSegment->setMiddleOffset(
0390                     previousSegment->startOffset() +
0391                     (m_temporallyDeletedHandleInfo.leftMiddleOffset - previousSegment->startOffset()) /
0392                     (m_temporallyDeletedHandleInfo.leftEndPointOffset - previousSegment->startOffset()) *
0393                     previousSegment->length()
0394                 );
0395                 nextSegment->setStartType(m_temporallyDeletedHandleInfo.rightEndPointType);
0396                 nextSegment->setStartColor(m_temporallyDeletedHandleInfo.rightEndPointColor);
0397                 nextSegment->setInterpolation(m_temporallyDeletedHandleInfo.rightInterpolationType);
0398                 nextSegment->setColorInterpolation(m_temporallyDeletedHandleInfo.rightColorInterpolationType);
0399                 nextSegment->setMiddleOffset(
0400                     nextSegment->startOffset() +
0401                     (m_temporallyDeletedHandleInfo.rightMiddleOffset - m_temporallyDeletedHandleInfo.rightEndPointOffset) /
0402                     (nextSegment->endOffset() - m_temporallyDeletedHandleInfo.rightEndPointOffset) *
0403                     nextSegment->length()
0404                 );
0405                 m_selectedHandle.type = HandleType_Stop;
0406                 m_selectedHandle.index = m_temporallyDeletedHandleInfo.handle.index;
0407                 m_temporallyDeletedHandleInfo.handle.type = HandleType_None;
0408             }
0409         }
0410 
0411         if (m_selectedHandle.type == HandleType_Segment) {
0412             if (m_temporallyDeletedHandleInfo.handle.type == HandleType_None) {
0413                 KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
0414                 if (m_gradient->segments().size() > 1 && m_selectedHandle.index > 0 && m_selectedHandle.index < m_gradient->segments().size() - 1 &&
0415                     !augmentedRect.contains(e->pos())) {
0416                     m_temporallyDeletedHandleInfo.handle.type = HandleType_Segment;
0417                     m_temporallyDeletedHandleInfo.handle.index = m_selectedHandle.index;
0418                     m_temporallyDeletedHandleInfo.leftEndPointType = segment->startType();
0419                     m_temporallyDeletedHandleInfo.leftEndPointOffset = segment->startOffset();
0420                     m_temporallyDeletedHandleInfo.leftEndPointColor = segment->startColor();
0421                     m_temporallyDeletedHandleInfo.rightEndPointType = segment->endType();
0422                     m_temporallyDeletedHandleInfo.rightEndPointOffset = segment->endOffset();
0423                     m_temporallyDeletedHandleInfo.rightEndPointColor = segment->endColor();
0424                     m_temporallyDeletedHandleInfo.leftInterpolationType = segment->interpolation();
0425                     m_temporallyDeletedHandleInfo.leftColorInterpolationType = segment->colorInterpolation();
0426                     m_temporallyDeletedHandleInfo.leftMiddleOffset = segment->middleOffset();
0427                     m_gradient->collapseSegment(m_gradient->segments()[m_selectedHandle.index]);
0428                     m_selectedHandle.type = HandleType_None;
0429                 } else {
0430                     KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
0431                     KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0432                     KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[m_selectedHandle.index + 1];
0433                     if (previousSegment && nextSegment) {
0434                         const qreal midPointRelativePos = segment->middleOffset() - segment->startOffset();
0435                         const qreal previousMidPointLocalPos =
0436                             previousSegment->length() > std::numeric_limits<qreal>::epsilon()
0437                             ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
0438                             : 0.0;
0439                         const qreal nextMidPointLocalPos =
0440                             nextSegment->length() > std::numeric_limits<qreal>::epsilon()
0441                             ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
0442                             : 0.0;
0443                         qreal newStartOffset, newEndOffset;
0444                         if (t < m_dragT) {
0445                             newStartOffset = qMax(t - m_relativeDragOffset, previousSegment->startOffset() + shrinkEpsilon);
0446                             newEndOffset = newStartOffset + segment->length();
0447                         } else {
0448                             newEndOffset = qMin(t + (segment->length() - m_relativeDragOffset), nextSegment->endOffset() - shrinkEpsilon);
0449                             newStartOffset = newEndOffset - segment->length();
0450                         }
0451                         previousSegment->setEndOffset(newStartOffset);
0452                         segment->setStartOffset(newStartOffset);
0453                         segment->setEndOffset(newEndOffset);
0454                         nextSegment->setStartOffset(newEndOffset);
0455                         previousSegment->setMiddleOffset(
0456                             previousSegment->startOffset() +
0457                             previousMidPointLocalPos * previousSegment->length()
0458                         );
0459                         nextSegment->setMiddleOffset(
0460                             nextSegment->startOffset() +
0461                             nextMidPointLocalPos * nextSegment->length()
0462                         );
0463                         segment->setMiddleOffset(segment->startOffset() + midPointRelativePos);
0464                     } else {
0465                         if (!previousSegment) {
0466                             segment->setStartOffset(0.0);
0467                         }
0468                         if (!nextSegment) {
0469                             segment->setEndOffset(1.0);
0470                         }
0471                     }
0472                 }
0473                 emit selectedHandleChanged();
0474                 emit updateRequested();
0475             }
0476 
0477         } else if (m_selectedHandle.type == HandleType_Stop) {
0478             if (m_temporallyDeletedHandleInfo.handle.type == HandleType_None) {
0479                 KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0480                 KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
0481                 if (m_gradient->segments().size() > 1 && m_selectedHandle.index > 0 && m_selectedHandle.index < m_gradient->segments().size() &&
0482                     !augmentedRect.contains(e->pos())) {
0483                     m_temporallyDeletedHandleInfo.handle.type = HandleType_Stop;
0484                     m_temporallyDeletedHandleInfo.handle.index = m_selectedHandle.index;
0485                     m_temporallyDeletedHandleInfo.leftEndPointType = previousSegment->endType();
0486                     m_temporallyDeletedHandleInfo.leftEndPointOffset = previousSegment->endOffset();
0487                     m_temporallyDeletedHandleInfo.leftEndPointColor = previousSegment->endColor();
0488                     m_temporallyDeletedHandleInfo.leftInterpolationType = previousSegment->interpolation();
0489                     m_temporallyDeletedHandleInfo.leftColorInterpolationType = previousSegment->colorInterpolation();
0490                     m_temporallyDeletedHandleInfo.leftMiddleOffset = previousSegment->middleOffset();
0491                     m_temporallyDeletedHandleInfo.rightEndPointType = nextSegment->startType();
0492                     m_temporallyDeletedHandleInfo.rightEndPointOffset = nextSegment->startOffset();
0493                     m_temporallyDeletedHandleInfo.rightEndPointColor = nextSegment->startColor();
0494                     m_temporallyDeletedHandleInfo.rightInterpolationType = nextSegment->interpolation();
0495                     m_temporallyDeletedHandleInfo.rightColorInterpolationType = nextSegment->colorInterpolation();
0496                     m_temporallyDeletedHandleInfo.rightMiddleOffset = nextSegment->middleOffset();
0497                     previousSegment->setEndType(nextSegment->endType());
0498                     previousSegment->setEndColor(nextSegment->endColor());
0499                     deleteHandleImpl(m_selectedHandle);
0500                     m_selectedHandle.type = HandleType_None;
0501                 } else {
0502                     KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0503                     KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
0504                     if (previousSegment && nextSegment) {
0505                         const qreal previousMidPointLocalPos =
0506                             previousSegment->length() > std::numeric_limits<qreal>::epsilon()
0507                             ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
0508                             : 0.0;
0509                         const qreal nextMidPointLocalPos =
0510                             nextSegment->length() > std::numeric_limits<qreal>::epsilon()
0511                             ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
0512                             : 0.0;
0513                         qreal newOffset;
0514                         if (t < m_dragT) {
0515                             newOffset = qMax(t, previousSegment->startOffset() + shrinkEpsilon);
0516                         } else {
0517                             newOffset = qMin(t, nextSegment->endOffset() - shrinkEpsilon);
0518                         }
0519                         previousSegment->setEndOffset(newOffset);
0520                         nextSegment->setStartOffset(newOffset);
0521                         previousSegment->setMiddleOffset(
0522                             previousSegment->startOffset() +
0523                             previousMidPointLocalPos * previousSegment->length()
0524                         );
0525                         nextSegment->setMiddleOffset(
0526                             nextSegment->startOffset() +
0527                             nextMidPointLocalPos * nextSegment->length()
0528                         );
0529                     } else {
0530                         if (!previousSegment) {
0531                             nextSegment->setStartOffset(0.0);
0532                         }
0533                         if (!nextSegment) {
0534                             previousSegment->setEndOffset(1.0);
0535                         }
0536                     }
0537                 }
0538                 emit selectedHandleChanged();
0539                 emit updateRequested();
0540             }
0541 
0542         } else if (m_selectedHandle.type == HandleType_MidPoint) {
0543             KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
0544             segment->setMiddleOffset(qBound(segment->startOffset(), t, segment->endOffset()));
0545             emit selectedHandleChanged();
0546             emit updateRequested();
0547         }
0548 
0549     } else {
0550         const QRect handlesRect = handlesStripeRect();
0551         Handle hoveredHandle;
0552         const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
0553         for (int i = 0; i < m_gradient->segments().size(); ++i) {
0554             KoGradientSegment *segment = m_gradient->segments()[i];
0555             // Check if a knob was hovered
0556             if (qAbs(t - segment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0557                 // Left knob was hovered
0558                 hoveredHandle.type = HandleType_Stop;
0559                 hoveredHandle.index = i;
0560                 break;
0561             } else if (qAbs(t - segment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0562                 // Right knob was hovered
0563                 hoveredHandle.type = HandleType_Stop;
0564                 hoveredHandle.index = i + 1;
0565                 break;
0566             } else if (qAbs(t - segment->middleOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) {
0567                 // middle knob was hovered
0568                 hoveredHandle.type = HandleType_MidPoint;
0569                 hoveredHandle.index = i;
0570                 break;
0571             } else if (t >= segment->startOffset() && t <= segment->endOffset()) {
0572                 // the segment area was hovered
0573                 hoveredHandle.type = HandleType_Segment;
0574                 hoveredHandle.index = i;
0575                 break;
0576             }
0577         }
0578         m_hoveredHandle = hoveredHandle;
0579         emit updateRequested();
0580     }
0581 }
0582 
0583 void KisSegmentGradientSlider::mouseDoubleClickEvent(QMouseEvent *e)
0584 {
0585     if (e->button() != Qt::LeftButton) {
0586         QWidget::mouseDoubleClickEvent(e);
0587         return;
0588     }
0589 
0590     const QRect rect = sliderRect();
0591     const QRect handlesRect = handlesStripeRect();
0592     const qreal t = (e->x() - rect.left()) / static_cast<qreal>(rect.width());
0593     const qreal handleClickTolerance = m_handleSize.width() / static_cast<qreal>(rect.width());
0594     const KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0595     const KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
0596 
0597     if ((previousSegment && qAbs(t - previousSegment->endOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y()) ||
0598         (nextSegment && qAbs(t - nextSegment->startOffset()) <= handleClickTolerance && e->pos().y() >= handlesRect.y())) {
0599         chooseSelectedStopColor();
0600     }
0601 }
0602 
0603 void KisSegmentGradientSlider::selectPreviousHandle()
0604 {
0605     if (m_selectedHandle.type == HandleType_Segment) {
0606         m_selectedHandle.type = HandleType_Stop;
0607         emit selectedHandleChanged();
0608         emit updateRequested();
0609     } else if (m_selectedHandle.type == HandleType_Stop) {
0610         if (m_selectedHandle.index > 0) {
0611             m_selectedHandle.type = HandleType_MidPoint;
0612             --m_selectedHandle.index;
0613             emit selectedHandleChanged();
0614             emit updateRequested();
0615         }
0616     } else if (m_selectedHandle.type == HandleType_MidPoint) {
0617         m_selectedHandle.type = HandleType_Segment;
0618         emit selectedHandleChanged();
0619         emit updateRequested();
0620     }
0621 }
0622 
0623 void KisSegmentGradientSlider::selectNextHandle()
0624 {
0625     if (m_selectedHandle.type == HandleType_Segment) {
0626         m_selectedHandle.type = HandleType_MidPoint;
0627         emit selectedHandleChanged();
0628         emit updateRequested();
0629     } else if (m_selectedHandle.type == HandleType_Stop) {
0630         if (m_selectedHandle.index < m_gradient->segments().size()) {
0631             m_selectedHandle.type = HandleType_Segment;
0632             emit selectedHandleChanged();
0633             emit updateRequested();
0634         }
0635     } else if (m_selectedHandle.type == HandleType_MidPoint) {
0636         m_selectedHandle.type = HandleType_Stop;
0637         ++m_selectedHandle.index;
0638         emit selectedHandleChanged();
0639         emit updateRequested();
0640     }
0641 }
0642 
0643 void KisSegmentGradientSlider::handleIncrementInput(int direction, Qt::KeyboardModifiers modifiers)
0644 {
0645     if (direction == 0) {
0646         return;
0647     }
0648     if (modifiers & Qt::ControlModifier) {
0649         if (direction < 0) {
0650             selectPreviousHandle();
0651         } else {
0652             selectNextHandle();
0653         }
0654     } else {
0655         const qreal increment = modifiers & Qt::ShiftModifier ? 0.001 : 0.01;
0656         moveSelectedHandle(direction < 0 ? -increment : increment);
0657     }
0658 }
0659 
0660 void KisSegmentGradientSlider::wheelEvent(QWheelEvent *e)
0661 {
0662     if (e->angleDelta().y() != 0) {
0663         handleIncrementInput(e->angleDelta().y(), e->modifiers());
0664         e->accept();
0665     } else {
0666         QWidget::wheelEvent(e);
0667     }
0668 }
0669 
0670 void KisSegmentGradientSlider::keyPressEvent(QKeyEvent *e)
0671 {
0672     switch (e->key()) {
0673     case Qt::Key_Left:
0674         handleIncrementInput(-1, e->modifiers());
0675         break;
0676     case Qt::Key_Right:
0677         handleIncrementInput(1, e->modifiers());
0678         break;
0679     case Qt::Key_Return:
0680     case Qt::Key_Enter:
0681         chooseSelectedStopColor();
0682         break;
0683     case Qt::Key_Delete:
0684         deleteSelectedHandle();
0685         break;
0686     default:
0687         QWidget::keyPressEvent(e);
0688         break;
0689     }
0690 }
0691 
0692 void KisSegmentGradientSlider::leaveEvent(QEvent *e)
0693 {
0694     m_hoveredHandle = {};
0695     emit updateRequested();
0696     QWidget::leaveEvent(e);
0697 }
0698 
0699 void KisSegmentGradientSlider::moveHandle(Handle handle, qreal distance, bool useShrinkEpsilon)
0700 {
0701     const qreal epsilon = useShrinkEpsilon ? shrinkEpsilon : 0.0; 
0702     if (handle.type == HandleType_Segment) {
0703         KoGradientSegment *segment = m_gradient->segments()[handle.index];
0704         KoGradientSegment *previousSegment = handle.index == 0 ? nullptr : m_gradient->segments()[handle.index - 1];
0705         KoGradientSegment *nextSegment = handle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[handle.index + 1];
0706         if (previousSegment && nextSegment) {
0707             const qreal midPointRelativePos = segment->middleOffset() - segment->startOffset();
0708             const qreal previousMidPointLocalPos =
0709                 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
0710                 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
0711                 : 0.0;
0712             const qreal nextMidPointLocalPos =
0713                 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
0714                 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
0715                 : 0.0;
0716             qreal newStartOffset, newEndOffset;
0717             if (distance < 0.0) {
0718                 newStartOffset = qMax(segment->startOffset() + distance, previousSegment->startOffset() + epsilon);
0719                 newEndOffset = newStartOffset + segment->length();
0720             } else {
0721                 newEndOffset = qMin(segment->endOffset() + distance, nextSegment->endOffset() - epsilon);
0722                 newStartOffset = newEndOffset - segment->length();
0723             }
0724             previousSegment->setEndOffset(newStartOffset);
0725             segment->setStartOffset(newStartOffset);
0726             segment->setEndOffset(newEndOffset);
0727             nextSegment->setStartOffset(newEndOffset);
0728             previousSegment->setMiddleOffset(
0729                 previousSegment->startOffset() +
0730                 previousMidPointLocalPos * previousSegment->length()
0731             );
0732             nextSegment->setMiddleOffset(
0733                 nextSegment->startOffset() +
0734                 nextMidPointLocalPos * nextSegment->length()
0735             );
0736             segment->setMiddleOffset(segment->startOffset() + midPointRelativePos);
0737         } else {
0738             if (!previousSegment) {
0739                 segment->setStartOffset(0.0);
0740             }
0741             if (!nextSegment) {
0742                 segment->setEndOffset(1.0);
0743             }
0744         }
0745         emit selectedHandleChanged();
0746         emit updateRequested();
0747     } else if (handle.type == HandleType_Stop) {
0748         KoGradientSegment *previousSegment = handle.index == 0 ? nullptr : m_gradient->segments()[handle.index - 1];
0749         KoGradientSegment *nextSegment = handle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[handle.index];
0750         if (previousSegment && nextSegment) {
0751             const qreal previousMidPointLocalPos =
0752                 previousSegment->length() > std::numeric_limits<qreal>::epsilon()
0753                 ? (previousSegment->middleOffset() - previousSegment->startOffset()) / previousSegment->length()
0754                 : 0.0;
0755             const qreal nextMidPointLocalPos =
0756                 nextSegment->length() > std::numeric_limits<qreal>::epsilon()
0757                 ? (nextSegment->middleOffset() - nextSegment->startOffset()) / nextSegment->length()
0758                 : 0.0;
0759             qreal newOffset;
0760             if (distance < 0) {
0761                 newOffset = qMax(previousSegment->endOffset() + distance, previousSegment->startOffset() + epsilon);
0762             } else {
0763                 newOffset = qMin(previousSegment->endOffset() + distance, nextSegment->endOffset() - epsilon);
0764             }
0765             previousSegment->setEndOffset(newOffset);
0766             nextSegment->setStartOffset(newOffset);
0767             previousSegment->setMiddleOffset(
0768                 previousSegment->startOffset() +
0769                 previousMidPointLocalPos * previousSegment->length()
0770             );
0771             nextSegment->setMiddleOffset(
0772                 nextSegment->startOffset() +
0773                 nextMidPointLocalPos * nextSegment->length()
0774             );
0775         } else {
0776             if (!previousSegment) {
0777                 nextSegment->setStartOffset(0.0);
0778             }
0779             if (!nextSegment) {
0780                 previousSegment->setEndOffset(1.0);
0781             }
0782         }
0783         emit selectedHandleChanged();
0784         emit updateRequested();
0785     } else if (handle.type == HandleType_MidPoint) {
0786         KoGradientSegment *segment = m_gradient->segments()[handle.index];
0787         segment->setMiddleOffset(qBound(segment->startOffset(), segment->middleOffset() + distance, segment->endOffset()));
0788         emit selectedHandleChanged();
0789         emit updateRequested();
0790     }
0791 }
0792 
0793 void KisSegmentGradientSlider::moveHandleLeft(Handle handle, qreal distance, bool useShrinkEpsilon)
0794 {
0795     moveHandle(handle, -distance, useShrinkEpsilon);
0796 }
0797 
0798 void KisSegmentGradientSlider::moveHandleRight(Handle handle, qreal distance, bool useShrinkEpsilon)
0799 {
0800     moveHandle(handle, distance, useShrinkEpsilon);
0801 }
0802 
0803 void KisSegmentGradientSlider::moveSelectedHandle(qreal distance, bool useShrinkEpsilon)
0804 {
0805     moveHandle(m_selectedHandle, distance, useShrinkEpsilon);
0806 }
0807 
0808 void KisSegmentGradientSlider::moveSelectedHandleLeft(qreal distance, bool useShrinkEpsilon)
0809 {
0810     moveSelectedHandle(-distance, useShrinkEpsilon);
0811 }
0812 
0813 void KisSegmentGradientSlider::moveSelectedHandleRight(qreal distance, bool useShrinkEpsilon)
0814 {
0815     moveSelectedHandle(distance, useShrinkEpsilon);
0816 }
0817 
0818 bool KisSegmentGradientSlider::deleteHandleImpl(Handle handle)
0819 {
0820     if (handle.type == HandleType_Segment) {
0821         if (m_gradient->removeSegment(m_gradient->segments()[handle.index])) {
0822             if (m_selectedHandle.index > 0) {
0823                 --m_selectedHandle.index;
0824             }
0825             return true;
0826         }
0827     } else if (m_selectedHandle.type == HandleType_Stop) {
0828         if (m_selectedHandle.index <= 0 || m_selectedHandle.index >= m_gradient->segments().size()) {
0829             return false;
0830         }
0831         KoGradientSegment *previousSegment = m_gradient->segments()[m_selectedHandle.index - 1];
0832         KoGradientSegment *nextSegment = m_gradient->segments()[m_selectedHandle.index];
0833         const qreal middleOffset = previousSegment->endOffset();
0834         previousSegment->setEndType(nextSegment->endType());
0835         previousSegment->setEndColor(nextSegment->endColor());
0836         m_gradient->removeSegment(nextSegment);
0837         previousSegment->setMiddleOffset(middleOffset);
0838         m_selectedHandle.type = HandleType_Segment;
0839         m_selectedHandle.index = m_selectedHandle.index - 1;
0840         return true;
0841     }
0842     return false;
0843 }
0844 
0845 void KisSegmentGradientSlider::deleteHandle(Handle handle)
0846 {
0847     if (deleteHandleImpl(handle)) {
0848         emit selectedHandleChanged();
0849         emit updateRequested();
0850     }
0851 }
0852 
0853 void KisSegmentGradientSlider::deleteSelectedHandle()
0854 {
0855     deleteHandle(m_selectedHandle);
0856 }
0857 
0858 void KisSegmentGradientSlider::collapseSelectedSegment()
0859 {
0860     if (m_selectedHandle.type != HandleType_Segment) {
0861         return;
0862     }
0863     if (m_gradient->collapseSegment(m_gradient->segments()[m_selectedHandle.index])) {
0864         if (m_selectedHandle.index == m_gradient->segments().size()) {
0865             --m_selectedHandle.index;
0866         }
0867         emit selectedHandleChanged();
0868         emit updateRequested();
0869     }
0870 }
0871 
0872 void KisSegmentGradientSlider::centerSelectedHandle() 
0873 {
0874     if (m_selectedHandle.type == HandleType_Segment) {
0875         KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
0876         KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0877         KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() - 1 ? nullptr : m_gradient->segments()[m_selectedHandle.index + 1];
0878         if (previousSegment && nextSegment) {
0879             moveSelectedHandle(
0880                 (previousSegment->startOffset() + nextSegment->endOffset()) / 2.0 -
0881                 (segment->startOffset() + segment->endOffset()) / 2.0);
0882         }
0883     } else if (m_selectedHandle.type == HandleType_Stop) {
0884         KoGradientSegment *previousSegment = m_selectedHandle.index == 0 ? nullptr : m_gradient->segments()[m_selectedHandle.index - 1];
0885         KoGradientSegment *nextSegment = m_selectedHandle.index == m_gradient->segments().size() ? nullptr : m_gradient->segments()[m_selectedHandle.index];
0886         if (previousSegment && nextSegment) {
0887             moveSelectedHandle((previousSegment->startOffset() + nextSegment->endOffset()) / 2.0 - nextSegment->startOffset());
0888         }
0889     } else if (m_selectedHandle.type == HandleType_MidPoint) {
0890         KoGradientSegment *segment = m_gradient->segments()[m_selectedHandle.index];
0891         qDebug() << segment->startOffset() << segment->endOffset() << segment->middleOffset() <<
0892                 ((segment->startOffset() + segment->endOffset()) / 2);
0893         moveSelectedHandle((segment->startOffset() + segment->endOffset()) / 2.0 - segment->middleOffset());
0894     }
0895 }
0896 
0897 void KisSegmentGradientSlider::splitSelectedSegment()
0898 {
0899     if (m_selectedHandle.type != HandleType_Segment) {
0900         return;
0901     }
0902     m_gradient->splitSegment(m_gradient->segments()[m_selectedHandle.index]);
0903     emit selectedHandleChanged();
0904     emit updateRequested();
0905 }
0906 
0907 void KisSegmentGradientSlider::duplicateSelectedSegment()
0908 {
0909     if (m_selectedHandle.type != HandleType_Segment) {
0910         return;
0911     }
0912     m_gradient->duplicateSegment(m_gradient->segments()[m_selectedHandle.index]);
0913     emit selectedHandleChanged();
0914     emit updateRequested();
0915 }
0916 
0917 void KisSegmentGradientSlider::mirrorSelectedSegment()
0918 {
0919     if (m_selectedHandle.type != HandleType_Segment) {
0920         return;
0921     }
0922     m_gradient->mirrorSegment(m_gradient->segments()[m_selectedHandle.index]);
0923     emit selectedHandleChanged();
0924     emit updateRequested();
0925 }
0926 
0927 void KisSegmentGradientSlider::flipGradient()
0928 {
0929     QList<KoGradientSegment*> oldSegments = m_gradient->segments();
0930     QList<KoGradientSegment*> newSegments;
0931     for (int i = oldSegments.size() - 1; i >= 0; --i) {
0932         KoGradientSegment* oldSegment = oldSegments[i];
0933         int interpolation = oldSegment->interpolation();
0934         int colorInterpolation = oldSegment->colorInterpolation();
0935 
0936         if (interpolation == INTERP_SPHERE_INCREASING) {
0937             interpolation = INTERP_SPHERE_DECREASING;
0938         }
0939         else if (interpolation == INTERP_SPHERE_DECREASING) {
0940             interpolation = INTERP_SPHERE_INCREASING;
0941         }
0942         if (colorInterpolation == COLOR_INTERP_HSV_CW) {
0943             colorInterpolation = COLOR_INTERP_HSV_CCW;
0944         }
0945         else if (colorInterpolation == COLOR_INTERP_HSV_CCW) {
0946             colorInterpolation = COLOR_INTERP_HSV_CW;
0947         }
0948 
0949         KoGradientSegment* newSegment = new KoGradientSegment(
0950             interpolation, colorInterpolation,
0951             { 1.0 - oldSegment->endOffset(), oldSegment->endColor(), oldSegment->endType() },
0952             { 1.0 - oldSegment->startOffset(), oldSegment->startColor(), oldSegment->startType() },
0953             1.0 - oldSegment->middleOffset()
0954         );
0955 
0956         newSegments.push_back(newSegment);
0957     }
0958     m_gradient->setSegments(newSegments);
0959     if (m_selectedHandle.type == HandleType_Stop) {
0960         m_selectedHandle.index = newSegments.size() - m_selectedHandle.index;
0961     } else {
0962         m_selectedHandle.index = newSegments.size() - 1 - m_selectedHandle.index;
0963     }
0964     emit selectedHandleChanged();
0965     emit updateRequested();
0966 }
0967 
0968 void KisSegmentGradientSlider::distributeStopsEvenly()
0969 {
0970     const qreal size = 1.0 / m_gradient->segments().size();
0971     for (int i = 0; i < m_gradient->segments().size(); ++i) {
0972         KoGradientSegment *segment = m_gradient->segments()[i];
0973         const qreal relativeMidPointPosition =
0974             (segment->middleOffset() - segment->startOffset()) /
0975             (segment->endOffset() - segment->startOffset());
0976         segment->setStartOffset(i * size);
0977         segment->setEndOffset((i + 1) * size);
0978         segment->setMiddleOffset(
0979             segment->startOffset() + relativeMidPointPosition *
0980             (segment->endOffset() - segment->startOffset()));
0981     }
0982     emit selectedHandleChanged();
0983     emit updateRequested();
0984 }
0985 
0986 QRect KisSegmentGradientSlider::sliderRect() const
0987 {
0988     const qreal handleWidthOverTwo = static_cast<qreal>(m_handleSize.width()) / 2.0;
0989     const int hMargin = static_cast<int>(std::ceil(handleWidthOverTwo)) + 2;
0990     return rect().adjusted(hMargin, 0, -hMargin, 0);
0991 }
0992 
0993 QRect KisSegmentGradientSlider::gradientStripeRect() const
0994 {
0995     const QRect rc = sliderRect();
0996     return rc.adjusted(0, 0, 0, -m_handleSize.height() - 4);
0997 }
0998 
0999 QRect KisSegmentGradientSlider::handlesStripeRect() const
1000 {
1001     const QRect rc = sliderRect();
1002     return rc.adjusted(0, rc.height() - (m_handleSize.height() + 2), 0, -2);
1003 }
1004 
1005 void KisSegmentGradientSlider::updateHandleSize()
1006 {
1007     QFontMetrics fm(font());
1008     const int h = qMax(15, static_cast<int>(std::ceil(fm.height() * 0.75)));
1009     m_handleSize = QSize(h * 0.75, h);
1010 }
1011 
1012 int KisSegmentGradientSlider::minimalHeight() const
1013 {
1014     QFontMetrics fm(font());
1015     const int h = fm.height();
1016 
1017     QStyleOptionToolButton opt;
1018     QSize sz = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(h, h), this);
1019 
1020     return qMax(32, sz.height()) + m_handleSize.height();
1021 }
1022 
1023 QSize KisSegmentGradientSlider::sizeHint() const
1024 {
1025     const int h = minimalHeight();
1026     return QSize(2 * h, h);
1027 }
1028 
1029 QSize KisSegmentGradientSlider::minimumSizeHint() const
1030 {
1031     const int h = minimalHeight();
1032     return QSize(h, h);
1033 }
1034 
1035 void KisSegmentGradientSlider::chooseSelectedStopColor()
1036 {
1037     if (m_selectedHandle.type != HandleType_Stop) {
1038         return;
1039     }
1040     QList<KoGradientSegment*> segments = m_gradient->segments();
1041     if (m_selectedHandle.index < 0 || m_selectedHandle.index > segments.size()) {
1042         return;
1043     }
1044 
1045     KoColor color1, color2;
1046     KoGradientSegmentEndpointType endType1{COLOR_ENDPOINT}, endType2{COLOR_ENDPOINT};
1047     if (m_selectedHandle.index == 0) {
1048         endType1 = segments[0]->startType();
1049         color1 = segments[0]->startColor();
1050     } else {
1051         endType1 = segments[m_selectedHandle.index - 1]->endType();
1052         color1 = segments[m_selectedHandle.index - 1]->endColor();
1053         if (m_selectedHandle.index < segments.size()) {
1054             endType2 = segments[m_selectedHandle.index]->startType();
1055             color2 = segments[m_selectedHandle.index]->startColor();
1056         }
1057     }
1058 
1059     KConfigGroup cfg =  KSharedConfig::openConfig()->group("colorselector");
1060     bool usePlatformDialog = cfg.readEntry("UsePlatformColorDialog", false);
1061     QDialog *colorDialog = nullptr;
1062 
1063     if (!usePlatformDialog) {
1064         KisDlgInternalColorSelector::Config cfg;
1065         KisDlgInternalColorSelector *dialog = new KisDlgInternalColorSelector(this, color1, cfg, i18n("Choose a color"));
1066         dialog->setPreviousColor(color1);
1067         auto setColorFn = [dialog, segments, this]() mutable
1068                         {
1069                             if (m_selectedHandle.index == 0) {
1070                                 segments[0]->setStartType(COLOR_ENDPOINT);
1071                                 segments[0]->setStartColor(dialog->getCurrentColor());
1072                             } else {
1073                                 segments[m_selectedHandle.index - 1]->setEndType(COLOR_ENDPOINT);
1074                                 segments[m_selectedHandle.index - 1]->setEndColor(dialog->getCurrentColor());
1075                                 if (m_selectedHandle.index < segments.size()) {
1076                                     segments[m_selectedHandle.index]->setStartType(COLOR_ENDPOINT);
1077                                     segments[m_selectedHandle.index]->setStartColor(dialog->getCurrentColor());
1078                                 }
1079                             }
1080                             emit selectedHandleChanged();
1081                             emit updateRequested();
1082                         };
1083         connect(dialog, &KisDlgInternalColorSelector::signalForegroundColorChosen, setColorFn);
1084         connect(dialog, &QDialog::accepted, setColorFn);
1085         colorDialog = dialog;
1086     } else {
1087         QColorDialog *dialog = new QColorDialog(this);
1088         dialog->setCurrentColor(color1.toQColor());
1089         auto setColorFn = [dialog, segments, this]() mutable
1090                         {
1091                             KoColor color;
1092                             color.fromQColor(dialog->currentColor());
1093                             if (m_selectedHandle.index == 0) {
1094                                 segments[0]->setStartType(COLOR_ENDPOINT);
1095                                 segments[0]->setStartColor(color);
1096                             } else {
1097                                 segments[m_selectedHandle.index - 1]->setEndType(COLOR_ENDPOINT);
1098                                 segments[m_selectedHandle.index - 1]->setEndColor(color);
1099                                 if (m_selectedHandle.index < segments.size()) {
1100                                     segments[m_selectedHandle.index]->setStartType(COLOR_ENDPOINT);
1101                                     segments[m_selectedHandle.index]->setStartColor(color);
1102                                 }
1103                             }
1104                             emit selectedHandleChanged();
1105                             emit updateRequested();
1106                         };
1107         connect(dialog, &QColorDialog::currentColorChanged, setColorFn);
1108         connect(dialog, &QDialog::accepted, setColorFn);
1109         colorDialog = dialog;
1110     }
1111     // common functionality
1112     connect(colorDialog, &QDialog::rejected, [endType1, endType2, color1, color2, segments, this]()
1113                                              {
1114                                                  if (m_selectedHandle.index == 0) {
1115                                                      segments[0]->setStartType(endType1);
1116                                                      segments[0]->setStartColor(color1);
1117                                                  } else {
1118                                                      segments[m_selectedHandle.index - 1]->setEndType(endType1);
1119                                                      segments[m_selectedHandle.index - 1]->setEndColor(color1);
1120                                                      if (m_selectedHandle.index < segments.size()) {
1121                                                          segments[m_selectedHandle.index]->setStartType(endType2);
1122                                                          segments[m_selectedHandle.index]->setStartColor(color2);
1123                                                      }
1124                                                  }
1125                                                  emit selectedHandleChanged();
1126                                                  emit updateRequested();
1127                                              });
1128     colorDialog->setAttribute(Qt::WA_DeleteOnClose);
1129     colorDialog->show();
1130     colorDialog->raise();
1131     colorDialog->activateWindow();
1132 }