Krita Source Code Documentation
Loading...
Searching...
No Matches
KisStopGradientEditor.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2016 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 <QSpinBox>
11#include <QDoubleSpinBox>
12#include <QPoint>
13#include <QMenu>
14#include <QAction>
15#include <QDialog>
16
17#include <KoColorSpace.h>
19
20#include "kis_debug.h"
21#include <kis_signals_blocker.h>
22
23#include <kis_icon_utils.h>
24
27
29
31 : QWidget(parent),
32 m_gradient(0)
33{
34 setupUi(this);
35
36 QAction *selectPreviousStopAction = new QAction(KisIconUtils::loadIcon("arrow-left"), i18nc("Button to select previous stop in the stop gradient editor", "Select previous stop"), this);
37 selectPreviousStopAction->setToolTip(selectPreviousStopAction->text());
38 connect(selectPreviousStopAction, SIGNAL(triggered()), gradientSlider, SLOT(selectPreviousStop()));
39
40 QAction *selectNextStopAction = new QAction(KisIconUtils::loadIcon("arrow-right"), i18nc("Button to select next stop in the stop gradient editor", "Select next stop"), this);
41 selectNextStopAction->setToolTip(selectNextStopAction->text());
42 connect(selectNextStopAction, SIGNAL(triggered()), gradientSlider, SLOT(selectNextStop()));
43
44 m_editStopAction = new QAction(KisIconUtils::loadIcon("document-edit"), i18nc("Button to edit the selected stop color in the stop gradient editor", "Edit stop"), this);
45 m_editStopAction->setToolTip(m_editStopAction->text());
46 connect(m_editStopAction, SIGNAL(triggered()), this, SLOT(editSelectedStop()));
47
48 m_deleteStopAction = new QAction(KisIconUtils::loadIcon("edit-delete"), i18nc("Button to delete the selected stop in the stop gradient editor", "Delete stop"), this);
49 m_deleteStopAction->setToolTip(m_deleteStopAction->text());
50 connect(m_deleteStopAction, SIGNAL(triggered()), gradientSlider, SLOT(deleteSelectedStop()));
51
52 QAction *flipStopsAction = new QAction(KisIconUtils::loadIcon("transform_icons_mirror_x"), i18nc("Button to flip the stops in the stop gradient editor", "Flip gradient"), this);
53 flipStopsAction->setToolTip(flipStopsAction->text());
54 connect(flipStopsAction, SIGNAL(triggered()), this, SLOT(reverse()));
55
56 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);
57 sortByValueAction->setToolTip(sortByValueAction->text());
58 connect(sortByValueAction, &QAction::triggered, this, [this]{ this->sortByValue(SORT_ASCENDING); } );
59
60 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);
61 sortByHueAction->setToolTip(sortByHueAction->text());
62 connect(sortByHueAction, &QAction::triggered, this, [this]{ this->sortByHue(SORT_ASCENDING); } );
63
64 QAction *distributeEvenlyAction = new QAction(KisIconUtils::loadIcon("distribute-horizontal"), i18nc("Button to evenly distribute the stops in the stop gradient editor", "Distribute stops evenly"), this);
65 distributeEvenlyAction->setToolTip(distributeEvenlyAction->text());
66 connect(distributeEvenlyAction, SIGNAL(triggered()), this, SLOT(distributeStopsEvenly()));
67
68 selectPreviousStopButton->setAutoRaise(true);
69 selectPreviousStopButton->setDefaultAction(selectPreviousStopAction);
70
71 selectNextStopButton->setAutoRaise(true);
72 selectNextStopButton->setDefaultAction(selectNextStopAction);
73
74 deleteStopButton->setAutoRaise(true);
75 deleteStopButton->setDefaultAction(m_deleteStopAction);
76
77 flipStopsButton->setAutoRaise(true);
78 flipStopsButton->setDefaultAction(flipStopsAction);
79
80 sortByValueButton->setAutoRaise(true);
81 sortByValueButton->setDefaultAction(sortByValueAction);
82
83 sortByHueButton->setAutoRaise(true);
84 sortByHueButton->setDefaultAction(sortByHueAction);
85
86 distributeEvenlyButton->setAutoRaise(true);
87 distributeEvenlyButton->setDefaultAction(distributeEvenlyAction);
88
89 compactModeSelectPreviousStopButton->setAutoRaise(true);
90 compactModeSelectPreviousStopButton->setDefaultAction(selectPreviousStopAction);
91
92 compactModeSelectNextStopButton->setAutoRaise(true);
93 compactModeSelectNextStopButton->setDefaultAction(selectNextStopAction);
94
95 compactModeMiscOptionsButton->setPopupMode(QToolButton::InstantPopup);
96 compactModeMiscOptionsButton->setArrowVisible(false);
97 compactModeMiscOptionsButton->setAutoRaise(true);
98 compactModeMiscOptionsButton->setIcon(KisIconUtils::loadIcon("view-choose"));
99 QMenu *compactModeMiscOptionsButtonMenu = new QMenu(this);
100 QAction *separator = new QAction(this);
101 separator->setSeparator(true);
102 compactModeMiscOptionsButtonMenu->addAction(m_editStopAction);
103 compactModeMiscOptionsButtonMenu->addAction(m_deleteStopAction);
104 compactModeMiscOptionsButtonMenu->addAction(separator);
105 compactModeMiscOptionsButtonMenu->addAction(flipStopsAction);
106 compactModeMiscOptionsButtonMenu->addAction(sortByValueAction);
107 compactModeMiscOptionsButtonMenu->addAction(sortByHueAction);
108 compactModeMiscOptionsButtonMenu->addAction(distributeEvenlyAction);
109 compactModeMiscOptionsButton->setPopupWidget(compactModeMiscOptionsButtonMenu);
110
111 stopEditor->setUseTransParentCheckBox(false);
112
113 connect(gradientSlider, SIGNAL(sigSelectedStop(int)), this, SLOT(stopChanged(int)));
114 connect(nameedit, SIGNAL(editingFinished()), this, SLOT(nameChanged()));
115 connect(stopEditor, SIGNAL(colorChanged(KoColor)), SLOT(colorChanged(KoColor)));
116 connect(stopEditor, SIGNAL(colorTypeChanged(KisGradientWidgetsUtils::ColorType)), this, SLOT(stopTypeChanged(KisGradientWidgetsUtils::ColorType)));
117 connect(stopEditor, SIGNAL(opacityChanged(qreal)), this, SLOT(opacityChanged(qreal)));
118 connect(stopEditor, SIGNAL(positionChanged(qreal)), this, SLOT(positionChanged(qreal)));
119
120 setCompactMode(false);
121
122 setGradient(0);
123 stopChanged(-1);
124}
125
126KisStopGradientEditor::KisStopGradientEditor(KoStopGradientSP gradient, QWidget *parent, const char* name, const QString& caption,
127 KoCanvasResourcesInterfaceSP canvasResourcesInterface)
128 : KisStopGradientEditor(parent)
129{
131 setObjectName(name);
132 setWindowTitle(caption);
133 setGradient(gradient);
134}
135
137{
138 lblName->setVisible(!value);
139 nameedit->setVisible(!value);
140 buttonsContainer->setVisible(!value);
141 stopEditorContainer->setVisible(!value);
142 compactModeButtonsContainer->setVisible(value);
143}
144
146{
147 m_gradient = gradient;
148 setEnabled(bool(m_gradient));
149
150 if (m_gradient) {
151 nameedit->setText(gradient->name());
152 gradientSlider->setGradientResource(m_gradient);
153 // stopChanged(gradientSlider->selectedStop());
154 }
155
156 Q_EMIT sigGradientChanged();
157}
158
163
168
170{
171 if (stopEditor->colorType() == KisGradientWidgetsUtils::Custom) {
172
173 stopEditor->setColor(color);
174 }
175}
176
177boost::optional<KoColor> KisStopGradientEditor::currentActiveStopColor() const
178{
179 if (stopEditor->colorType() != KisGradientWidgetsUtils::Custom) return boost::none;
180 return stopEditor->color();
181}
182
184{
185 if (!m_gradient) return;
186
187 const bool hasStopSelected = stop >= 0;
188
189 m_editStopAction->setEnabled(hasStopSelected);
190 m_deleteStopAction->setEnabled(hasStopSelected && m_gradient->stops().size() > 2);
191 stopEditorContainer->setCurrentIndex(hasStopSelected ? 0 : 1);
192
193 if (hasStopSelected) {
194 selectedStopLabel->setText(i18nc("Text that indicates the selected stop in the stop gradient editor", "Stop #%1", stop + 1));
195
196 KoGradientStop gradientStop = m_gradient->stops()[stop];
197 KisSignalsBlocker blocker(stopEditor);
198 stopEditor->setPosition(gradientStop.position * 100.0);
199
200 KoColor color;
201 qreal opacity;
202 KoGradientStopType type = gradientStop.type;
203
204 if (type == FOREGROUNDSTOP) {
205 stopEditor->setColorType(KisGradientWidgetsUtils::Foreground);
208 } else {
209 color = gradientStop.color;
210 }
211 opacity = 100.0;
212 }
213 else if (type == BACKGROUNDSTOP) {
214 stopEditor->setColorType(KisGradientWidgetsUtils::Background);
217 } else {
218 color = gradientStop.color;
219 }
220 opacity = 100.0;
221 }
222 else {
223 stopEditor->setColorType(KisGradientWidgetsUtils::Custom);
224 color = gradientStop.color;
225 opacity = color.opacityF() * 100.0;
226 }
227
228 stopEditor->setColor(color);
229 stopEditor->setOpacity(opacity);
230
231 } else {
232 selectedStopLabel->setText(i18nc("Text that indicates no stop is selected in the stop gradient editor", "No stop selected"));
233 }
234
235 Q_EMIT sigGradientChanged();
236}
237
239 QList<KoGradientStop> stops = m_gradient->stops();
240 int currentStop = gradientSlider->selectedStop();
241 KoGradientStop stop = stops[currentStop];
242
244 stop.type = FOREGROUNDSTOP;
247 }
248 } else if (type == KisGradientWidgetsUtils::Background) {
249 stop.type = BACKGROUNDSTOP;
252 }
253 } else {
254 stop.type = COLORSTOP;
255 }
256
257 stop.color.setOpacity(1.0);
258
259 stops.removeAt(currentStop);
260 stops.insert(currentStop, stop);
261 m_gradient->setStops(stops);
262 stopEditor->setColor(stop.color);
263 stopEditor->setOpacity(100.0);
264 Q_EMIT gradientSlider->updateRequested(); //setSelectedStopType(type);
265 Q_EMIT sigGradientChanged();
266}
267
269{
270 if (!m_gradient) return;
271
272 QList<KoGradientStop> stops = m_gradient->stops();
273 int currentStop = gradientSlider->selectedStop();
274 KoGradientStop stop = stops[currentStop];
275
276 KoColor c(color);
277 c.setOpacity(stop.color.opacityU8());
278 stop.color = c;
279
280 stops.removeAt(currentStop);
281 stops.insert(currentStop, stop);
282 m_gradient->setStops(stops);
283
284 Q_EMIT gradientSlider->updateRequested();
285 Q_EMIT sigGradientChanged();
286}
287
289{
290 if (!m_gradient) return;
291
292 QList<KoGradientStop> stops = m_gradient->stops();
293 int currentStop = gradientSlider->selectedStop();
294 KoGradientStop stop = stops[currentStop];
295
296 stop.color.setOpacity(value / 100.0);
297
298 stops.removeAt(currentStop);
299 stops.insert(currentStop, stop);
300 m_gradient->setStops(stops);
301
302 Q_EMIT gradientSlider->updateRequested();
303 Q_EMIT sigGradientChanged();
304}
305
307{
308 if (!m_gradient) return;
309
310 QList<KoGradientStop> stops = m_gradient->stops();
311 int currentStop = gradientSlider->selectedStop();
312 KoGradientStop stop = stops[currentStop];
313 stop.position = value / 100.0;
314
315 stops.removeAt(currentStop);
316 {
317 currentStop = 0;
318 for (int i = 0; i < stops.size(); i++) {
319 if (stop.position <= stops[i].position) break;
320
321 currentStop = i + 1;
322 }
323 }
324 stops.insert(currentStop, stop);
325 m_gradient->setStops(stops);
326 gradientSlider->setSelectedStop(currentStop);
327
328 Q_EMIT gradientSlider->updateRequested();
329 Q_EMIT sigGradientChanged();
330}
331
333{
334 if (!m_gradient) return;
335
336 m_gradient->setName(nameedit->text());
337 m_gradient->setFilename(nameedit->text() + m_gradient->defaultFileExtension());
338 Q_EMIT sigGradientChanged();
339}
340
342{
343 if (!m_gradient) return;
344
345 QList<KoGradientStop> stops = m_gradient->stops();
346 QList<KoGradientStop> reversedStops;
347 for(const KoGradientStop& stop : stops) {
348 reversedStops.push_front(KoGradientStop(1 - stop.position, stop.color, stop.type));
349 }
350 m_gradient->setStops(reversedStops);
351 if (gradientSlider->selectedStop() >= 0) {
352 gradientSlider->setSelectedStop(stops.size() - 1 - gradientSlider->selectedStop());
353 } else {
354 Q_EMIT gradientSlider->updateRequested();
355 }
356
357 Q_EMIT sigGradientChanged();
358}
359
361{
362 if (!m_gradient) return;
363
364 QList<KoGradientStop> stops = m_gradient->stops();
365 qreal spacing = 1.0 / static_cast<qreal>(stops.size() - 1);
366 for (int i = 0; i < stops.size(); ++i) {
367 stops[i].position = qBound(0.0, static_cast<qreal>(i) * spacing, 1.0);
368 }
369 m_gradient->setStops(stops);
370 if (gradientSlider->selectedStop() >= 0) {
371 stopEditor->setPosition(stops[gradientSlider->selectedStop()].position * 100.0);
372 }
373 Q_EMIT gradientSlider->updateRequested();
374 Q_EMIT sigGradientChanged();
375}
376
377void KisStopGradientEditor::sortByValue( SortFlags flags = SORT_ASCENDING )
378{
379 if (!m_gradient) return;
380
381 bool ascending = (flags & SORT_ASCENDING) > 0;
382 bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0;
383
384 QList<KoGradientStop> stops = m_gradient->stops();
385 const int stopCount = stops.size();
386
387 QList<KoGradientStop> sortedStops;
388 std::sort(stops.begin(), stops.end(), KoGradientStopValueSort());
389
390 int stopIndex = 0;
391 for (const KoGradientStop& stop : stops) {
392 const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : stop.color.toQColor().valueF();
393 const float position = ascending ? value : 1.f - value;
394
395 if (ascending) {
396 sortedStops.push_back(KoGradientStop(position, stop.color, stop.type));
397 } else {
398 sortedStops.push_front(KoGradientStop(position, stop.color, stop.type));
399 }
400
401 stopIndex++;
402 }
403
404 m_gradient->setStops(sortedStops);
405 gradientSlider->setSelectedStop(stopCount - 1);
406
407 Q_EMIT gradientSlider->updateRequested();
408 Q_EMIT sigGradientChanged();
409}
410
411void KisStopGradientEditor::sortByHue( SortFlags flags = SORT_ASCENDING )
412{
413 if (!m_gradient) return;
414
415 bool ascending = (flags & SORT_ASCENDING) > 0;
416 bool evenDistribution = (flags & EVEN_DISTRIBUTION) > 0;
417
418 QList<KoGradientStop> stops = m_gradient->stops();
419 const int stopCount = stops.size();
420
421 QList<KoGradientStop> sortedStops;
422 std::sort(stops.begin(), stops.end(), KoGradientStopHueSort());
423
424 int stopIndex = 0;
425 for (const KoGradientStop& stop : stops) {
426 const float value = evenDistribution ? (float)stopIndex / (float)(stopCount - 1) : qMax(0.0, stop.color.toQColor().hueF());
427 const float position = ascending ? value : 1.f - value;
428
429 if (ascending) {
430 sortedStops.push_back(KoGradientStop(position, stop.color, stop.type));
431 } else {
432 sortedStops.push_front(KoGradientStop(position, stop.color, stop.type));
433 }
434
435 stopIndex++;
436 }
437
438 m_gradient->setStops(sortedStops);
439 gradientSlider->setSelectedStop(stopCount - 1);
440
441 Q_EMIT gradientSlider->updateRequested();
442 Q_EMIT sigGradientChanged();
443}
444
446{
447 if (gradientSlider->selectedStop() < 0) {
448 return;
449 }
450
451 QDialog *dialog = new QDialog(this);
452 dialog->setModal(true);
453 dialog->setWindowTitle(i18nc("Title for the gradient stop editor", "Edit Stop"));
454 dialog->setAttribute(Qt::WA_DeleteOnClose);
455
456 QWidget *editor = stopEditorContainer->currentWidget();
457 int index = stopEditorContainer->indexOf(editor);
458 stopEditorContainer->removeWidget(editor);
459
460 QVBoxLayout *dialogLayout = new QVBoxLayout;
461 dialogLayout->setContentsMargins(10, 10, 10, 10);
462 dialogLayout->addWidget(editor);
463
464 dialog->setLayout(dialogLayout);
465 editor->show();
466 dialog->resize(0, 0);
467
468 connect(dialog, &QDialog::finished, [this, editor, index](int)
469 {
470 stopEditorContainer->insertWidget(index, editor);
471 stopEditorContainer->setCurrentIndex(index);
472 });
473
474 dialog->show();
475 dialog->raise();
476 dialog->activateWindow();
477}
float value(const T *src, size_t ch)
KoGradientStopType
@ FOREGROUNDSTOP
@ BACKGROUNDSTOP
@ COLORSTOP
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void stopTypeChanged(KisGradientWidgetsUtils::ColorType type)
KoCanvasResourcesInterfaceSP m_canvasResourcesInterface
void sortByValue(SortFlags flags)
void colorChanged(const KoColor &color)
boost::optional< KoColor > currentActiveStopColor() const
KisStopGradientEditor(QWidget *parent)
void setGradient(KoStopGradientSP gradient)
void setCanvasResourcesInterface(KoCanvasResourcesInterfaceSP canvasResourcesInterface)
KoCanvasResourcesInterfaceSP canvasResourcesInterface() const
void sortByHue(SortFlags flags)
void notifyGlobalColorChanged(const KoColor &color)
qreal opacityF() const
Definition KoColor.cpp:345
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
quint8 opacityU8() const
Definition KoColor.cpp:341
QIcon loadIcon(const QString &name)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
KoGradientStopType type