File indexing completed on 2024-12-22 04:13:07
0001 /* 0002 * SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2016 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 <QSpinBox> 0011 #include <QDoubleSpinBox> 0012 #include <QPoint> 0013 #include <QMenu> 0014 #include <QAction> 0015 #include <QDialog> 0016 0017 #include <KoColorSpace.h> 0018 #include <resources/KoStopGradient.h> 0019 0020 #include "kis_debug.h" 0021 #include <kis_signals_blocker.h> 0022 0023 #include <kis_icon_utils.h> 0024 0025 #include <KoCanvasResourcesIds.h> 0026 #include <KoCanvasResourcesInterface.h> 0027 0028 #include "KisStopGradientEditor.h" 0029 0030 KisStopGradientEditor::KisStopGradientEditor(QWidget *parent) 0031 : QWidget(parent), 0032 m_gradient(0) 0033 { 0034 setupUi(this); 0035 0036 QAction *selectPreviousStopAction = new QAction(KisIconUtils::loadIcon("arrow-left"), i18nc("Button to select previous stop in the stop gradient editor", "Select previous stop"), this); 0037 selectPreviousStopAction->setToolTip(selectPreviousStopAction->text()); 0038 connect(selectPreviousStopAction, SIGNAL(triggered()), gradientSlider, SLOT(selectPreviousStop())); 0039 0040 QAction *selectNextStopAction = new QAction(KisIconUtils::loadIcon("arrow-right"), i18nc("Button to select next stop in the stop gradient editor", "Select next stop"), this); 0041 selectNextStopAction->setToolTip(selectNextStopAction->text()); 0042 connect(selectNextStopAction, SIGNAL(triggered()), gradientSlider, SLOT(selectNextStop())); 0043 0044 m_editStopAction = new QAction(KisIconUtils::loadIcon("document-edit"), i18nc("Button to edit the selected stop color in the stop gradient editor", "Edit stop"), this); 0045 m_editStopAction->setToolTip(m_editStopAction->text()); 0046 connect(m_editStopAction, SIGNAL(triggered()), this, SLOT(editSelectedStop())); 0047 0048 m_deleteStopAction = new QAction(KisIconUtils::loadIcon("edit-delete"), i18nc("Button to delete the selected stop in the stop gradient editor", "Delete stop"), this); 0049 m_deleteStopAction->setToolTip(m_deleteStopAction->text()); 0050 connect(m_deleteStopAction, SIGNAL(triggered()), gradientSlider, SLOT(deleteSelectedStop())); 0051 0052 QAction *flipStopsAction = new QAction(KisIconUtils::loadIcon("transform_icons_mirror_x"), i18nc("Button to flip the stops in the stop gradient editor", "Flip gradient"), this); 0053 flipStopsAction->setToolTip(flipStopsAction->text()); 0054 connect(flipStopsAction, SIGNAL(triggered()), this, SLOT(reverse())); 0055 0056 QAction *sortByValueAction = new QAction(KisIconUtils::loadIcon("sort-by-value"), i18nc("Button to sort the stops by value in the stop gradient editor", "Sort stops by value"), this); 0057 sortByValueAction->setToolTip(sortByValueAction->text()); 0058 connect(sortByValueAction, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING); } ); 0059 0060 QAction *sortByHueAction = new QAction(KisIconUtils::loadIcon("sort-by-hue"), i18nc("Button to sort the stops by hue in the stop gradient editor", "Sort stops by hue"), this); 0061 sortByHueAction->setToolTip(sortByHueAction->text()); 0062 connect(sortByHueAction, &QAction::triggered, this, [this]{ this->sortByHue(SORT_ASCENDING); } ); 0063 0064 QAction *distributeEvenlyAction = new QAction(KisIconUtils::loadIcon("distribute-horizontal"), i18nc("Button to evenly distribute the stops in the stop gradient editor", "Distribute stops evenly"), this); 0065 distributeEvenlyAction->setToolTip(distributeEvenlyAction->text()); 0066 connect(distributeEvenlyAction, SIGNAL(triggered()), this, SLOT(distributeStopsEvenly())); 0067 0068 selectPreviousStopButton->setAutoRaise(true); 0069 selectPreviousStopButton->setDefaultAction(selectPreviousStopAction); 0070 0071 selectNextStopButton->setAutoRaise(true); 0072 selectNextStopButton->setDefaultAction(selectNextStopAction); 0073 0074 deleteStopButton->setAutoRaise(true); 0075 deleteStopButton->setDefaultAction(m_deleteStopAction); 0076 0077 flipStopsButton->setAutoRaise(true); 0078 flipStopsButton->setDefaultAction(flipStopsAction); 0079 0080 sortByValueButton->setAutoRaise(true); 0081 sortByValueButton->setDefaultAction(sortByValueAction); 0082 0083 sortByHueButton->setAutoRaise(true); 0084 sortByHueButton->setDefaultAction(sortByHueAction); 0085 0086 distributeEvenlyButton->setAutoRaise(true); 0087 distributeEvenlyButton->setDefaultAction(distributeEvenlyAction); 0088 0089 compactModeSelectPreviousStopButton->setAutoRaise(true); 0090 compactModeSelectPreviousStopButton->setDefaultAction(selectPreviousStopAction); 0091 0092 compactModeSelectNextStopButton->setAutoRaise(true); 0093 compactModeSelectNextStopButton->setDefaultAction(selectNextStopAction); 0094 0095 compactModeMiscOptionsButton->setPopupMode(QToolButton::InstantPopup); 0096 compactModeMiscOptionsButton->setArrowVisible(false); 0097 compactModeMiscOptionsButton->setAutoRaise(true); 0098 compactModeMiscOptionsButton->setIcon(KisIconUtils::loadIcon("view-choose")); 0099 QMenu *compactModeMiscOptionsButtonMenu = new QMenu(this); 0100 QAction *separator = new QAction(this); 0101 separator->setSeparator(true); 0102 compactModeMiscOptionsButtonMenu->addAction(m_editStopAction); 0103 compactModeMiscOptionsButtonMenu->addAction(m_deleteStopAction); 0104 compactModeMiscOptionsButtonMenu->addAction(separator); 0105 compactModeMiscOptionsButtonMenu->addAction(flipStopsAction); 0106 compactModeMiscOptionsButtonMenu->addAction(sortByValueAction); 0107 compactModeMiscOptionsButtonMenu->addAction(sortByHueAction); 0108 compactModeMiscOptionsButtonMenu->addAction(distributeEvenlyAction); 0109 compactModeMiscOptionsButton->setPopupWidget(compactModeMiscOptionsButtonMenu); 0110 0111 stopEditor->setUseTransParentCheckBox(false); 0112 0113 connect(gradientSlider, SIGNAL(sigSelectedStop(int)), this, SLOT(stopChanged(int))); 0114 connect(nameedit, SIGNAL(editingFinished()), this, SLOT(nameChanged())); 0115 connect(stopEditor, SIGNAL(colorChanged(KoColor)), SLOT(colorChanged(KoColor))); 0116 connect(stopEditor, SIGNAL(colorTypeChanged(KisGradientWidgetsUtils::ColorType)), this, SLOT(stopTypeChanged(KisGradientWidgetsUtils::ColorType))); 0117 connect(stopEditor, SIGNAL(opacityChanged(qreal)), this, SLOT(opacityChanged(qreal))); 0118 connect(stopEditor, SIGNAL(positionChanged(qreal)), this, SLOT(positionChanged(qreal))); 0119 0120 setCompactMode(false); 0121 0122 setGradient(0); 0123 stopChanged(-1); 0124 } 0125 0126 KisStopGradientEditor::KisStopGradientEditor(KoStopGradientSP gradient, QWidget *parent, const char* name, const QString& caption, 0127 KoCanvasResourcesInterfaceSP canvasResourcesInterface) 0128 : KisStopGradientEditor(parent) 0129 { 0130 m_canvasResourcesInterface = canvasResourcesInterface; 0131 setObjectName(name); 0132 setWindowTitle(caption); 0133 setGradient(gradient); 0134 } 0135 0136 void KisStopGradientEditor::setCompactMode(bool value) 0137 { 0138 lblName->setVisible(!value); 0139 nameedit->setVisible(!value); 0140 buttonsContainer->setVisible(!value); 0141 stopEditorContainer->setVisible(!value); 0142 compactModeButtonsContainer->setVisible(value); 0143 } 0144 0145 void KisStopGradientEditor::setGradient(KoStopGradientSP gradient) 0146 { 0147 m_gradient = gradient; 0148 setEnabled(m_gradient); 0149 0150 if (m_gradient) { 0151 nameedit->setText(gradient->name()); 0152 gradientSlider->setGradientResource(m_gradient); 0153 // stopChanged(gradientSlider->selectedStop()); 0154 } 0155 0156 emit sigGradientChanged(); 0157 } 0158 0159 void KisStopGradientEditor::setCanvasResourcesInterface(KoCanvasResourcesInterfaceSP canvasResourcesInterface) 0160 { 0161 m_canvasResourcesInterface = canvasResourcesInterface; 0162 } 0163 0164 KoCanvasResourcesInterfaceSP KisStopGradientEditor::canvasResourcesInterface() const 0165 { 0166 return m_canvasResourcesInterface; 0167 } 0168 0169 void KisStopGradientEditor::notifyGlobalColorChanged(const KoColor &color) 0170 { 0171 if (stopEditor->colorType() == KisGradientWidgetsUtils::Custom) { 0172 0173 stopEditor->setColor(color); 0174 } 0175 } 0176 0177 boost::optional<KoColor> KisStopGradientEditor::currentActiveStopColor() const 0178 { 0179 if (stopEditor->colorType() != KisGradientWidgetsUtils::Custom) return boost::none; 0180 return stopEditor->color(); 0181 } 0182 0183 void KisStopGradientEditor::stopChanged(int stop) 0184 { 0185 if (!m_gradient) return; 0186 0187 const bool hasStopSelected = stop >= 0; 0188 0189 m_editStopAction->setEnabled(hasStopSelected); 0190 m_deleteStopAction->setEnabled(hasStopSelected && m_gradient->stops().size() > 2); 0191 stopEditorContainer->setCurrentIndex(hasStopSelected ? 0 : 1); 0192 0193 if (hasStopSelected) { 0194 selectedStopLabel->setText(i18nc("Text that indicates the selected stop in the stop gradient editor", "Stop #%1", stop + 1)); 0195 0196 KoGradientStop gradientStop = m_gradient->stops()[stop]; 0197 KisSignalsBlocker blocker(stopEditor); 0198 stopEditor->setPosition(gradientStop.position * 100.0); 0199 0200 KoColor color; 0201 qreal opacity; 0202 KoGradientStopType type = gradientStop.type; 0203 0204 if (type == FOREGROUNDSTOP) { 0205 stopEditor->setColorType(KisGradientWidgetsUtils::Foreground); 0206 if (m_canvasResourcesInterface) { 0207 color = m_canvasResourcesInterface->resource(KoCanvasResource::ForegroundColor).value<KoColor>(); 0208 } else { 0209 color = gradientStop.color; 0210 } 0211 opacity = 100.0; 0212 } 0213 else if (type == BACKGROUNDSTOP) { 0214 stopEditor->setColorType(KisGradientWidgetsUtils::Background); 0215 if (m_canvasResourcesInterface) { 0216 color = m_canvasResourcesInterface->resource(KoCanvasResource::BackgroundColor).value<KoColor>(); 0217 } else { 0218 color = gradientStop.color; 0219 } 0220 opacity = 100.0; 0221 } 0222 else { 0223 stopEditor->setColorType(KisGradientWidgetsUtils::Custom); 0224 color = gradientStop.color; 0225 opacity = color.opacityF() * 100.0; 0226 } 0227 0228 stopEditor->setColor(color); 0229 stopEditor->setOpacity(opacity); 0230 0231 } else { 0232 selectedStopLabel->setText(i18nc("Text that indicates no stop is selected in the stop gradient editor", "No stop selected")); 0233 } 0234 0235 emit sigGradientChanged(); 0236 } 0237 0238 void KisStopGradientEditor::stopTypeChanged(KisGradientWidgetsUtils::ColorType type) { 0239 QList<KoGradientStop> stops = m_gradient->stops(); 0240 int currentStop = gradientSlider->selectedStop(); 0241 KoGradientStop stop = stops[currentStop]; 0242 0243 if (type == KisGradientWidgetsUtils::Foreground) { 0244 stop.type = FOREGROUNDSTOP; 0245 if (m_canvasResourcesInterface) { 0246 stop.color = m_canvasResourcesInterface->resource(KoCanvasResource::ForegroundColor).value<KoColor>(); 0247 } 0248 } else if (type == KisGradientWidgetsUtils::Background) { 0249 stop.type = BACKGROUNDSTOP; 0250 if (m_canvasResourcesInterface) { 0251 stop.color = m_canvasResourcesInterface->resource(KoCanvasResource::BackgroundColor).value<KoColor>(); 0252 } 0253 } else { 0254 stop.type = COLORSTOP; 0255 } 0256 0257 stop.color.setOpacity(1.0); 0258 0259 stops.removeAt(currentStop); 0260 stops.insert(currentStop, stop); 0261 m_gradient->setStops(stops); 0262 stopEditor->setColor(stop.color); 0263 stopEditor->setOpacity(100.0); 0264 emit gradientSlider->updateRequested(); //setSelectedStopType(type); 0265 emit sigGradientChanged(); 0266 } 0267 0268 void KisStopGradientEditor::colorChanged(const KoColor& color) 0269 { 0270 if (!m_gradient) return; 0271 0272 QList<KoGradientStop> stops = m_gradient->stops(); 0273 int currentStop = gradientSlider->selectedStop(); 0274 KoGradientStop stop = stops[currentStop]; 0275 0276 KoColor c(color); 0277 c.setOpacity(stop.color.opacityU8()); 0278 stop.color = c; 0279 0280 stops.removeAt(currentStop); 0281 stops.insert(currentStop, stop); 0282 m_gradient->setStops(stops); 0283 0284 emit gradientSlider->updateRequested(); 0285 emit sigGradientChanged(); 0286 } 0287 0288 void KisStopGradientEditor::opacityChanged(qreal value) 0289 { 0290 if (!m_gradient) return; 0291 0292 QList<KoGradientStop> stops = m_gradient->stops(); 0293 int currentStop = gradientSlider->selectedStop(); 0294 KoGradientStop stop = stops[currentStop]; 0295 0296 stop.color.setOpacity(value / 100.0); 0297 0298 stops.removeAt(currentStop); 0299 stops.insert(currentStop, stop); 0300 m_gradient->setStops(stops); 0301 0302 emit gradientSlider->updateRequested(); 0303 emit sigGradientChanged(); 0304 } 0305 0306 void KisStopGradientEditor::positionChanged(qreal value) 0307 { 0308 if (!m_gradient) return; 0309 0310 QList<KoGradientStop> stops = m_gradient->stops(); 0311 int currentStop = gradientSlider->selectedStop(); 0312 KoGradientStop stop = stops[currentStop]; 0313 stop.position = value / 100.0; 0314 0315 stops.removeAt(currentStop); 0316 { 0317 currentStop = 0; 0318 for (int i = 0; i < stops.size(); i++) { 0319 if (stop.position <= stops[i].position) break; 0320 0321 currentStop = i + 1; 0322 } 0323 } 0324 stops.insert(currentStop, stop); 0325 m_gradient->setStops(stops); 0326 gradientSlider->setSelectedStop(currentStop); 0327 0328 emit gradientSlider->updateRequested(); 0329 emit sigGradientChanged(); 0330 } 0331 0332 void KisStopGradientEditor::nameChanged() 0333 { 0334 if (!m_gradient) return; 0335 0336 m_gradient->setName(nameedit->text()); 0337 m_gradient->setFilename(nameedit->text() + m_gradient->defaultFileExtension()); 0338 emit sigGradientChanged(); 0339 } 0340 0341 void KisStopGradientEditor::reverse() 0342 { 0343 if (!m_gradient) return; 0344 0345 QList<KoGradientStop> stops = m_gradient->stops(); 0346 QList<KoGradientStop> reversedStops; 0347 for(const KoGradientStop& stop : stops) { 0348 reversedStops.push_front(KoGradientStop(1 - stop.position, stop.color, stop.type)); 0349 } 0350 m_gradient->setStops(reversedStops); 0351 if (gradientSlider->selectedStop() >= 0) { 0352 gradientSlider->setSelectedStop(stops.size() - 1 - gradientSlider->selectedStop()); 0353 } else { 0354 emit gradientSlider->updateRequested(); 0355 } 0356 0357 emit sigGradientChanged(); 0358 } 0359 0360 void KisStopGradientEditor::distributeStopsEvenly() 0361 { 0362 if (!m_gradient) return; 0363 0364 QList<KoGradientStop> stops = m_gradient->stops(); 0365 qreal spacing = 1.0 / static_cast<qreal>(stops.size() - 1); 0366 for (int i = 0; i < stops.size(); ++i) { 0367 stops[i].position = qBound(0.0, static_cast<qreal>(i) * spacing, 1.0); 0368 } 0369 m_gradient->setStops(stops); 0370 if (gradientSlider->selectedStop() >= 0) { 0371 stopEditor->setPosition(stops[gradientSlider->selectedStop()].position * 100.0); 0372 } 0373 emit gradientSlider->updateRequested(); 0374 emit sigGradientChanged(); 0375 } 0376 0377 void KisStopGradientEditor::sortByValue( SortFlags flags = SORT_ASCENDING ) 0378 { 0379 if (!m_gradient) return; 0380 0381 bool ascending = (flags & SORT_ASCENDING) > 0; 0382 bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0; 0383 0384 QList<KoGradientStop> stops = m_gradient->stops(); 0385 const int stopCount = stops.size(); 0386 0387 QList<KoGradientStop> sortedStops; 0388 std::sort(stops.begin(), stops.end(), KoGradientStopValueSort()); 0389 0390 int stopIndex = 0; 0391 for (const KoGradientStop& stop : stops) { 0392 const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.color.toQColor().valueF(); 0393 const float position = ascending ? value : 1.f - value; 0394 0395 if (ascending) { 0396 sortedStops.push_back(KoGradientStop(position, stop.color, stop.type)); 0397 } else { 0398 sortedStops.push_front(KoGradientStop(position, stop.color, stop.type)); 0399 } 0400 0401 stopIndex++; 0402 } 0403 0404 m_gradient->setStops(sortedStops); 0405 gradientSlider->setSelectedStop(stopCount - 1); 0406 0407 emit gradientSlider->updateRequested(); 0408 emit sigGradientChanged(); 0409 } 0410 0411 void KisStopGradientEditor::sortByHue( SortFlags flags = SORT_ASCENDING ) 0412 { 0413 if (!m_gradient) return; 0414 0415 bool ascending = (flags & SORT_ASCENDING) > 0; 0416 bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0; 0417 0418 QList<KoGradientStop> stops = m_gradient->stops(); 0419 const int stopCount = stops.size(); 0420 0421 QList<KoGradientStop> sortedStops; 0422 std::sort(stops.begin(), stops.end(), KoGradientStopHueSort()); 0423 0424 int stopIndex = 0; 0425 for (const KoGradientStop& stop : stops) { 0426 const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : qMax(0.0, stop.color.toQColor().hueF()); 0427 const float position = ascending ? value : 1.f - value; 0428 0429 if (ascending) { 0430 sortedStops.push_back(KoGradientStop(position, stop.color, stop.type)); 0431 } else { 0432 sortedStops.push_front(KoGradientStop(position, stop.color, stop.type)); 0433 } 0434 0435 stopIndex++; 0436 } 0437 0438 m_gradient->setStops(sortedStops); 0439 gradientSlider->setSelectedStop(stopCount - 1); 0440 0441 emit gradientSlider->updateRequested(); 0442 emit sigGradientChanged(); 0443 } 0444 0445 void KisStopGradientEditor::editSelectedStop() 0446 { 0447 if (gradientSlider->selectedStop() < 0) { 0448 return; 0449 } 0450 0451 QDialog *dialog = new QDialog(this); 0452 dialog->setModal(true); 0453 dialog->setWindowTitle(i18nc("Title for the gradient stop editor", "Edit Stop")); 0454 dialog->setAttribute(Qt::WA_DeleteOnClose); 0455 0456 QWidget *editor = stopEditorContainer->currentWidget(); 0457 int index = stopEditorContainer->indexOf(editor); 0458 stopEditorContainer->removeWidget(editor); 0459 0460 QVBoxLayout *dialogLayout = new QVBoxLayout; 0461 dialogLayout->setMargin(10); 0462 dialogLayout->addWidget(editor); 0463 0464 dialog->setLayout(dialogLayout); 0465 editor->show(); 0466 dialog->resize(0, 0); 0467 0468 connect(dialog, &QDialog::finished, [this, editor, index](int) 0469 { 0470 stopEditorContainer->insertWidget(index, editor); 0471 stopEditorContainer->setCurrentIndex(index); 0472 }); 0473 0474 dialog->show(); 0475 dialog->raise(); 0476 dialog->activateWindow(); 0477 }