Krita Source Code Documentation
Loading...
Searching...
No Matches
KoFillConfigWidget.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr)
3 * SPDX-FileCopyrightText: 2012 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
9
10#include <QToolButton>
11#include <QHBoxLayout>
12#include <QVBoxLayout>
13#include <QButtonGroup>
14#include <QSizePolicy>
15#include <QBitmap>
16#include <QAction>
17#include <QSharedPointer>
18#include <QMessageBox>
19
20#include <klocalizedstring.h>
21
22#include <KoIcon.h>
23#include <KoColor.h>
24#include <KoColorPopupAction.h>
26#include <KoSelection.h>
27#include <KoCanvasBase.h>
30#include <KoShape.h>
31#include <KoShapeController.h>
32#include <KoShapeBackground.h>
35#include <KoShapeStroke.h>
37#include <KoColorBackground.h>
39#include <KoPatternBackground.h>
41#include "KoZoomHandler.h"
42#include "KoColorPopupButton.h"
43#include "ui_KoFillConfigWidget.h"
44#include <kis_signals_blocker.h>
47#include <kis_assert.h>
49#include <KoStopGradient.h>
50#include <QInputDialog>
51#include <KoShapeFillWrapper.h>
52#include <functional>
53
55
56#include "kis_global.h"
57#include "kis_debug.h"
58
59static const char* const buttonnone[]={
60 "16 16 3 1",
61 "# c #000000",
62 "e c #ff0000",
63 "- c #ffffff",
64 "################",
65 "#--------------#",
66 "#-e----------e-#",
67 "#--e--------e--#",
68 "#---e------e---#",
69 "#----e----e----#",
70 "#-----e--e-----#",
71 "#------ee------#",
72 "#------ee------#",
73 "#-----e--e-----#",
74 "#----e----e----#",
75 "#---e------e---#",
76 "#--e--------e--#",
77 "#-e----------e-#",
78 "#--------------#",
79 "################"};
80
81static const char* const buttonsolid[]={
82 "16 16 2 1",
83 "# c #000000",
84 ". c #969696",
85 "################",
86 "#..............#",
87 "#..............#",
88 "#..............#",
89 "#..............#",
90 "#..............#",
91 "#..............#",
92 "#..............#",
93 "#..............#",
94 "#..............#",
95 "#..............#",
96 "#..............#",
97 "#..............#",
98 "#..............#",
99 "#..............#",
100 "################"};
101
102
103// FIXME: Smoother gradient button.
104
105static const char* const buttongradient[]={
106 "16 16 15 1",
107 "# c #000000",
108 "n c #101010",
109 "m c #202020",
110 "l c #303030",
111 "k c #404040",
112 "j c #505050",
113 "i c #606060",
114 "h c #707070",
115 "g c #808080",
116 "f c #909090",
117 "e c #a0a0a0",
118 "d c #b0b0b0",
119 "c c #c0c0c0",
120 "b c #d0d0d0",
121 "a c #e0e0e0",
122 "################",
123 "#abcdefghijklmn#",
124 "#abcdefghijklmn#",
125 "#abcdefghijklmn#",
126 "#abcdefghijklmn#",
127 "#abcdefghijklmn#",
128 "#abcdefghijklmn#",
129 "#abcdefghijklmn#",
130 "#abcdefghijklmn#",
131 "#abcdefghijklmn#",
132 "#abcdefghijklmn#",
133 "#abcdefghijklmn#",
134 "#abcdefghijklmn#",
135 "#abcdefghijklmn#",
136 "#abcdefghijklmn#",
137 "################"};
138
139static const char* const buttonpattern[]={
140 "16 16 4 1",
141 ". c #0a0a0a",
142 "# c #333333",
143 "a c #a0a0a0",
144 "b c #ffffffff",
145 "################",
146 "#aaaaabbbbaaaaa#",
147 "#aaaaabbbbaaaaa#",
148 "#aaaaabbbbaaaaa#",
149 "#aaaaabbbbaaaaa#",
150 "#aaaaabbbbaaaaa#",
151 "#bbbbbaaaabbbbb#",
152 "#bbbbbaaaabbbbb#",
153 "#bbbbbaaaabbbbb#",
154 "#bbbbbaaaabbbbb#",
155 "#aaaaabbbbaaaaa#",
156 "#aaaaabbbbaaaaa#",
157 "#aaaaabbbbaaaaa#",
158 "#aaaaabbbbaaaaa#",
159 "#aaaaabbbbaaaaa#",
160 "################"};
161
162using namespace std::placeholders;
163
164
165class Q_DECL_HIDDEN KoFillConfigWidget::Private
166{
167public:
169 : canvas(0),
170 colorChangedCompressor(100, std::bind(&KoFillConfigWidget::colorChanged, q, _1)),
171 gradientChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE),
172 shapeChangedCompressor(200,KisSignalCompressor::FIRST_ACTIVE),
173 fillVariant(_fillVariant),
174 noSelectionTrackingMode(false)
175 {
176 }
177
178 KoColorPopupAction *colorAction {nullptr};
179 KoResourcePopupAction *gradientAction {nullptr};
180 KoResourcePopupAction *patternAction {nullptr};
181 QButtonGroup *group {nullptr};
182
183 KoCanvasBase *canvas {nullptr};
184
188
193
194 bool noSelectionTrackingMode {false};
195
197 QScopedPointer<SvgMeshGradient> activeMeshGradient;
198
199 QScopedPointer<Ui_KoFillConfigWidget> ui;
200
201 std::vector<KisAcyclicSignalConnector::Blocker> deactivationLocks;
202
203 std::array<boost::optional<KoColor>, 2> overriddenColorFromProvider;
204};
205
206KoFillConfigWidget::KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent)
207 : QWidget(parent)
208 , d(new Private(fillVariant, this))
209{
210 d->canvas = canvas;
211
212 if (trackShapeSelection) {
213 connect(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), &d->shapeChangedCompressor,
214 SLOT(start()));
215 connect(&d->shapeChangedCompressor, SIGNAL(timeout()), this, SLOT(shapeChanged()));
216 }
217
218 d->resourceManagerAcyclicConnector.connectBackwardResourcePair(
219 d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
220 this, SLOT(slotCanvasResourceChanged(int,QVariant)));
221
222 d->resourceManagerAcyclicConnector.connectForwardVoid(
225
226 KisAcyclicSignalConnector *resetConnector = d->resourceManagerAcyclicConnector.createCoordinatedConnector();
227 resetConnector->connectForwardVoid(
230
231 // configure GUI
232
233 d->ui.reset(new Ui_KoFillConfigWidget());
234 d->ui->setupUi(this);
235
236 d->group = new QButtonGroup(this);
237 d->group->setExclusive(true);
238
239 d->ui->btnNoFill->setIcon(QPixmap((const char **) buttonnone));
240 d->group->addButton(d->ui->btnNoFill, None);
241
242 d->ui->btnSolidFill->setIcon(QPixmap((const char **) buttonsolid));
243 d->group->addButton(d->ui->btnSolidFill, Solid);
244
245 d->ui->btnGradientFill->setIcon(QPixmap((const char **) buttongradient));
246 d->group->addButton(d->ui->btnGradientFill, Gradient);
247
248 d->ui->btnPatternFill->setIcon(QPixmap((const char **) buttonpattern));
249 d->group->addButton(d->ui->btnPatternFill, Pattern);
250 d->ui->btnPatternFill->setVisible(false);
251
252 if (fillVariant == KoFlake::Fill) {
253 // FIXME: different button
254 d->ui->btnMeshFill->setIcon(QPixmap((const char**) buttonpattern));
255 d->group->addButton(d->ui->btnMeshFill, MeshGradient);
256 } else {
257 d->ui->btnMeshFill->setVisible(false);
258 }
259
260 d->colorAction = new KoColorPopupAction(d->ui->btnChooseSolidColor);
261 d->colorAction->setToolTip(i18n("Change the filling color"));
262 d->colorAction->setCurrentColor(Qt::white);
263
264 d->ui->btnChooseSolidColor->setDefaultAction(d->colorAction);
265 d->ui->btnChooseSolidColor->setPopupMode(QToolButton::InstantPopup);
266 d->ui->btnSolidColorSample->setIcon(KisIconUtils::loadIcon("krita_tool_color_sampler"));
267
268 // TODO: for now the color sampling button is disabled!
269 d->ui->btnSolidColorSample->setEnabled(false);
270 d->ui->btnSolidColorSample->setVisible(false);
271
272 connect(d->colorAction, &KoColorPopupAction::colorChanged, [this](KoColor color) {
273 d->colorChangedCompressor.start({color.toQColor(), d->fillVariant});
274 });
275
276 connect(d->ui->btnChooseSolidColor, SIGNAL(iconSizeChanged()), d->colorAction, SLOT(updateIcon()));
277
278 connect(d->group, SIGNAL(idClicked(int)), SLOT(styleButtonPressed(int)));
279
280 connect(d->group, SIGNAL(idClicked(int)), SLOT(slotUpdateFillTitle()));
281
282 slotUpdateFillTitle();
283 styleButtonPressed(d->group->checkedId());
284
285
286 // Gradient selector
287 d->ui->wdgGradientEditor->setCompactMode(true);
288 d->ui->wdgGradientEditor->setCanvasResourcesInterface(canvas->resourceManager()->canvasResourcesInterface());
289 connect(d->ui->wdgGradientEditor, SIGNAL(sigGradientChanged()), &d->gradientChangedCompressor, SLOT(start()));
290 connect(&d->gradientChangedCompressor, SIGNAL(timeout()), SLOT(activeGradientChanged()));
291
292 d->gradientAction = new KoResourcePopupAction(ResourceType::Gradients, canvas->resourceManager()->canvasResourcesInterface(), d->ui->btnChoosePredefinedGradient);
293
294 d->gradientAction->setToolTip(i18n("Change filling gradient"));
295 d->ui->btnChoosePredefinedGradient->setDefaultAction(d->gradientAction);
296 d->ui->btnChoosePredefinedGradient->setPopupMode(QToolButton::InstantPopup);
297
298 connect(d->gradientAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)),
299 SLOT(gradientResourceChanged()));
300 connect(d->ui->btnChoosePredefinedGradient, SIGNAL(iconSizeChanged()), d->gradientAction, SLOT(updateIcon()));
301
302 d->ui->btnSaveGradient->setIcon(KisIconUtils::loadIcon("document-save"));
303 connect(d->ui->btnSaveGradient, SIGNAL(clicked()), SLOT(slotSavePredefinedGradientClicked()));
304
305 connect(d->ui->cmbGradientRepeat, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientRepeatChanged()));
306 connect(d->ui->cmbGradientType, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientTypeChanged()));
307
308 // meshgradient
309 connect(d->ui->meshStopColorButton, SIGNAL(changed(const KoColor&)), this, SLOT(slotMeshHandleColorChanged(const KoColor&)));
310
311 d->ui->spinbRows->setRange(1, 20);
312 d->ui->spinbColumns->setRange(1, 20);
313 connect(d->ui->spinbRows, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged()));
314 connect(d->ui->spinbColumns, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged()));
315 connect(d->ui->cmbSmoothingType, SIGNAL(currentIndexChanged(int)), SLOT(slotMeshGradientShadingChanged(int)));
316
317 // initialize deactivation locks
318 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
319
320
321/*
322 // Pattern selector
323 d->patternAction = new KoResourcePopupAction(ResourceType::Patterns, d->colorButton);
324 d->patternAction->setToolTip(i18n("Change the filling pattern"));
325 connect(d->patternAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), this, SLOT(patternChanged(QSharedPointer<KoShapeBackground>)));
326 connect(d->colorButton, SIGNAL(iconSizeChanged()), d->patternAction, SLOT(updateIcon()));
327*/
328
329
330}
331
336
338{
339 KIS_SAFE_ASSERT_RECOVER_NOOP(!d->deactivationLocks.empty());
340 d->deactivationLocks.clear();
341
342 if (!d->noSelectionTrackingMode) {
343 d->shapeChangedCompressor.start();
344 } else {
346 }
347
349}
350
352{
354
355 KIS_SAFE_ASSERT_RECOVER_NOOP(d->deactivationLocks.empty());
356 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector));
357}
358
360{
361 d->shapeChangedCompressor.start();
362}
363
365{
366 d->meshposition = position;
368}
369
371{
372 d->noSelectionTrackingMode = value;
373 if (!d->noSelectionTrackingMode) {
374 d->shapeChangedCompressor.start();
375 }
376}
377
379{
380 QString text = d->group->checkedButton() ? d->group->checkedButton()->text() : QString();
381 text.replace('&', QString());
382 d->ui->lblFillTitle->setText(text);
383}
384
386{
389
390 KoColor color = value.value<KoColor>();
391
392 const int checkedId = d->group->checkedId();
393
394 if (checkedId < 0 || checkedId == None || checkedId == Solid) {
395
396 d->group->button(Solid)->setChecked(true);
397 d->selectedFillIndex = Solid;
398
402
403 if (key == d->fillVariant) {
404 d->colorAction->setCurrentColor(color);
405 }
406 colorChanged({color.toQColor(), colorSlot});
407 } else if (checkedId == Gradient && key == KoCanvasResource::ForegroundColor) {
408 d->ui->wdgGradientEditor->notifyGlobalColorChanged(color);
409 }
410 } else if (key == KoCanvasResource::CurrentGradient) {
411 KoResourceSP gradient = value.value<KoAbstractGradientSP>();
412 const int checkedId = d->group->checkedId();
413
414 if (gradient && (checkedId < 0 || checkedId == None || checkedId == Gradient)) {
415 d->group->button(Gradient)->setChecked(true);
416 d->gradientAction->setCurrentResource(gradient);
417 }
418 }
419}
420
425
427 return d->selectedFillIndex;
428}
429
431{
433
434 switch (buttonId) {
437 break;
439 colorChanged({d->colorAction->currentColor(), d->fillVariant});
440 break;
442 if (d->activeGradient) {
445 } else {
447 }
448 break;
450 // Only select mode in the widget, don't set actual pattern :/
451 //d->colorButton->setDefaultAction(d->patternAction);
452 //patternChanged(d->patternAction->currentBackground());
453 break;
455 if (d->activeMeshGradient) {
457 } else {
460 }
461 break;
462 }
463
464
465 // update tool option fields with first selected object
466 if (shapes.isEmpty() == false) {
467 KoShape *firstShape = shapes.first();
468 updateUiFromFillType(firstShape);
469 }
470
472}
473
475{
476 KoShapeStrokeSP stroke(new KoShapeStroke());
478
479 switch (d->group->checkedId()) {
481 stroke->setColor(Qt::transparent);
482 break;
484 stroke->setColor(d->colorAction->currentColor());
485 break;
487 QScopedPointer<QGradient> g(d->activeGradient->toQGradient());
488 QBrush newBrush = *g;
489 stroke->setLineBrush(newBrush);
490 stroke->setColor(Qt::transparent);
491 break;
492 }
494 break;
495 }
496
497 return stroke;
498}
499
501{
502 QList<KoShape*> selectedShapes = currentShapes();
503 if (selectedShapes.isEmpty()) {
504 Q_EMIT sigFillChanged();
505 return;
506 }
507
508 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
509 KUndo2Command *command = wrapper.setColor(QColor());
510
511 if (command) {
512 d->canvas->addCommand(command);
513 }
514
515 Q_EMIT sigFillChanged();
516}
517
518void KoFillConfigWidget::colorChanged(std::pair<QColor, KoFlake::FillVariant> resource)
519{
520 QColor color = resource.first;
521 KoFlake::FillVariant fillVariant = resource.second;
522 if (!color.isValid()) {
523 return;
524 }
525
526 QList<KoShape*> selectedShapes = currentShapes();
527 if (selectedShapes.isEmpty()) {
529 Q_EMIT sigFillChanged();
530 return;
531 }
532
533 d->overriddenColorFromProvider[fillVariant] = boost::none;
534
535 KoShapeFillWrapper wrapper(selectedShapes, fillVariant);
536
537 KUndo2Command *command = wrapper.setColor(color);
538 if (command) {
539 d->canvas->addCommand(command);
540 }
541
542 // only returns true if it is a stroke object that has a 0 for line width
543 if (wrapper.hasZeroLineWidth() ) {
544 KUndo2Command *lineCommand = wrapper.setLineWidth(1.0);
545 if (lineCommand) {
546 d->canvas->addCommand(lineCommand);
547 }
548
549 // * line to test out
550 QColor solidColor = d->colorAction->currentColor();
551 solidColor.setAlpha(255);
552 command = wrapper.setColor(solidColor);
553 if (command) {
554 d->canvas->addCommand(command);
555 }
556
557 }
558
559 Q_EMIT sigFillChanged();
561}
562
564{
565 const int checkedId = d->group->checkedId();
566
567 auto uploadColorToResourceManager = [this](KoCanvasResource::CanvasResourceId res,
568 KoFlake::FillVariant var, KoColor &color) {
569 if (!d->overriddenColorFromProvider[var]) {
570 d->overriddenColorFromProvider[var] =
571 d->canvas->resourceManager()->resource(res).value<KoColor>();
572 }
573
581 d->canvas->resourceManager()->setResource(res, QVariant::fromValue(color));
582 };
583
584 auto uploadColorFromShape = [&](KoCanvasResource::CanvasResourceId res, KoFlake::FillVariant fill) {
585 KoShapeFillWrapper wrapper(currentShapes(), fill);
586 if (!wrapper.color().isValid()) {
587 return;
588 }
589 KoColor color;
590 color.fromQColor(wrapper.color());
591 uploadColorToResourceManager(res, fill, color);
592 };
593
594 if (checkedId == Solid) {
595 if (currentShapes().isEmpty()) {
599 KoColor color = d->colorAction->currentKoColor();
600 uploadColorToResourceManager(res, d->fillVariant, color);
601 } else {
604 }
605 } else if (checkedId == Gradient) {
606 if (boost::optional<KoColor> gradientColor =
607 d->ui->wdgGradientEditor->currentActiveStopColor()) {
608 KoColor color = *gradientColor;
612 uploadColorToResourceManager(res, d->fillVariant, color);
613 }
614 }
615}
616
618{
619 auto checkAndRecover = [this](KoCanvasResource::CanvasResourceId res,
621 if (d->overriddenColorFromProvider[var]) {
622 d->canvas->resourceManager()->setResource(
623 res, QVariant::fromValue(*d->overriddenColorFromProvider[var]));
624 d->overriddenColorFromProvider[var] = boost::none;
625 }
626 };
627
630}
631
633{
635 auto server = serverProvider->gradientServer();
636
637 const QString defaultGradientNamePrefix = i18nc("default prefix for the saved gradient", "gradient");
638 const QString saveLocation = server->saveLocation();
639
640 QString name = d->activeGradient->name().isEmpty() ? defaultGradientNamePrefix : d->activeGradient->name();
641 QFileInfo fileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
642 bool fileOverWriteAccepted = false;
643
644 while(!fileOverWriteAccepted) {
645 name = QInputDialog::getText(this,
646 i18nc("@title:window", "Save Gradient"),
647 i18n("Enter gradient name:"),
648 QLineEdit::Normal, name);
649 if (name.isNull() || name.isEmpty()) {
650 return;
651 } else {
652 fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
653 if (fileInfo.exists()) {
654 int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
655 , i18n("The name '%1' already exists, do you wish to overwrite it?", name)
656 , QMessageBox::Yes | QMessageBox::No);
657 if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
658 } else {
659 fileOverWriteAccepted = true;
660 }
661 }
662 }
663
664 d->activeGradient->setName(name);
665 d->activeGradient->setFilename(name.split(" ").join("_") + d->activeGradient->defaultFileExtension());
666
667 KoAbstractGradientSP newGradient = d->activeGradient->clone().dynamicCast<KoAbstractGradient>();
668
670
671 d->gradientAction->setCurrentResource(newGradient);
672}
673
681
683{
685 qSharedPointerDynamicCast<KoGradientBackground>(
686 d->gradientAction->currentBackground());
687
688 updateGradientUi(bg->gradient());
689
692}
693
695{
696 QGradient::Type type =
697 d->ui->cmbGradientType->currentIndex() == 0 ?
698 QGradient::LinearGradient : QGradient::RadialGradient;
699
700 d->activeGradient->setType(type);
702}
703
705{
706 QGradient::Spread spread =
707 QGradient::Spread(d->ui->cmbGradientRepeat->currentIndex());
708
709 d->activeGradient->setSpread(spread);
711}
712
713void KoFillConfigWidget::updateGradientUi(const QGradient *gradient)
714{
715 KisSignalsBlocker b1(d->ui->wdgGradientEditor,
716 d->ui->cmbGradientType,
717 d->ui->cmbGradientRepeat);
718
719 d->ui->wdgGradientEditor->setGradient(0);
720
721 d->activeGradient = KoStopGradient::fromQGradient(gradient);
722
723 d->ui->wdgGradientEditor->setGradient(d->activeGradient);
724 d->ui->cmbGradientType->setCurrentIndex(d->activeGradient->type() != QGradient::LinearGradient);
725 d->ui->cmbGradientRepeat->setCurrentIndex(int(d->activeGradient->spread()));
726}
727
729{
730 QList<KoShape*> selectedShapes = currentShapes();
731 if (selectedShapes.isEmpty()) {
732 Q_EMIT sigFillChanged();
733 return;
734 }
735
736 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
737 QScopedPointer<QGradient> srcQGradient(d->activeGradient->toQGradient());
738 KUndo2Command *command = wrapper.applyGradientStopsOnly(srcQGradient.data());
739
740 if (command) {
741 d->canvas->addCommand(command);
742 }
743
744 Q_EMIT sigFillChanged();
745}
746
748{
749 bool savingEnabled = false;
750
751 QScopedPointer<QGradient> currentGradient(d->activeGradient->toQGradient());
752 QSharedPointer<KoShapeBackground> bg = d->gradientAction->currentBackground();
753 if (bg) {
754 QSharedPointer<KoGradientBackground> resourceBackground =
755 qSharedPointerDynamicCast<KoGradientBackground>(bg);
756
757 savingEnabled = resourceBackground->gradient()->stops() != currentGradient->stops();
758 savingEnabled |= resourceBackground->gradient()->type() != currentGradient->type();
759 savingEnabled |= resourceBackground->gradient()->spread() != currentGradient->spread();
760 }
761
762 d->ui->btnSaveGradient->setEnabled(savingEnabled);
763}
764
766{
767 Q_UNUSED(background);
768
769#if 0
770 QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(background);
771 if (! patternBackground) {
772 return;
773 }
774
775 QList<KoShape*> selectedShapes = currentShapes();
776 if (selectedShapes.isEmpty()) {
777 return;
778 }
779
781 fill->setPattern(patternBackground->pattern());
782 d->canvas->addCommand(new KoShapeBackgroundCommand(selectedShapes, fill));
783
784#endif
785}
786
794
796{
797 d->activeMeshGradient->setType(static_cast<SvgMeshGradient::Shading>(index));
799}
800
802{
803 QList<KoShape*> selectedShapes = currentShapes();
804 KIS_SAFE_ASSERT_RECOVER_RETURN(!selectedShapes.isEmpty());
805
806 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
807 const SvgMeshGradient *gradient = wrapper.meshgradient();
808
809 // if we changed the handle, the gradient *has* to exist
811
812 if (d->meshposition.isValid()) {
813 // We don't have any signals firing when we change the structure (i.e position of stops etc) of a
814 // meshgradient. So, activeMeshGradient isn't updated when this happens. Hence we update it here and
815 // then modify the color.
816 d->activeMeshGradient.reset(new SvgMeshGradient(*gradient));
817
818 d->activeMeshGradient->getMeshArray()->modifyColor(d->meshposition, c.toQColor());
820 }
821 return;
822}
823
825{
826 {
827 KoColor color = d->canvas->resourceManager()->foregroundColor();
828 if (d->group->checkedId() == -1 || d->group->checkedId() == None) {
829 d->group->button(Solid)->setChecked(true);
830 }
831 d->selectedFillIndex = Solid;
832 d->colorAction->setCurrentColor(color);
833 }
834
835 Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
836 button->setEnabled(true);
837 }
838
839 Q_EMIT sigFillChanged();
840}
841
843{
844 QList<KoShape*> selectedShapes = currentShapes();
845 if (selectedShapes.isEmpty()) {
846 return;
847 }
848
849 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
850 const SvgMeshGradient *g = wrapper.meshgradient();
851 if (g) {
852 d->activeMeshGradient.reset(new SvgMeshGradient(*g));
853 } else {
855 }
856
858}
859
861{
862 QList<KoShape*> selectedShapes = currentShapes();
863 if (selectedShapes.isEmpty()) {
864 return;
865 }
866
867 // use this for mesh creation
868 QSizeF maxSize;
869 for (const auto& shape: selectedShapes) {
870 QSizeF size = shape->boundingRect().size();
871 if (size.height() > maxSize.height()) {
872 maxSize.rheight() = size.height();
873 }
874 if (size.width() > maxSize.width()) {
875 maxSize.rwidth() = size.width();
876 }
877 }
878
879 SvgMeshGradient *gradient = new SvgMeshGradient;
880
881 QColor color = d->canvas->resourceManager()->resource(KoFlake::Background).value<KoColor>().toQColor();
882
883 int nrows = d->ui->spinbRows->value();
884 int ncols = d->ui->spinbColumns->value();
885
886 if (d->ui->cmbSmoothingType->currentIndex()) {
888 } else {
890 }
891
892 gradient->getMeshArray()->createDefaultMesh(nrows, ncols, color, maxSize);
894 d->activeMeshGradient.reset(gradient);
895}
896
898{
899 QList<KoShape*> selectedShapes = currentShapes();
900 // if called by "manager"
901 if (selectedShapes.isEmpty()) {
902 Q_EMIT sigFillChanged();
903 return;
904 }
905
906 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant);
907
908 KUndo2Command *command = wrapper.setMeshGradient(d->activeMeshGradient.data(), QTransform());
909 if (command) {
910 d->canvas->addCommand(command);
911 }
912
913 Q_EMIT sigFillChanged();
914}
915
917{
918 if (!d->activeMeshGradient) return;
919
920 KisSignalsBlocker b(d->ui->spinbRows,
921 d->ui->spinbColumns,
922 d->ui->cmbSmoothingType,
923 d->ui->meshStopColorButton);
924
925 SvgMeshArray *mesharray = d->activeMeshGradient->getMeshArray().data();
926 d->ui->spinbRows->setValue(mesharray->numRows());
927 d->ui->spinbColumns->setValue(mesharray->numColumns());
928 d->ui->cmbSmoothingType->setCurrentIndex(d->activeMeshGradient->type());
929 if (d->meshposition.isValid()) {
930 QColor qc = d->activeMeshGradient->getMeshArray()->getStop(d->meshposition).color;
931
932 KoColor c = d->ui->meshStopColorButton->color();
933 c.fromQColor(qc);
934
935 d->ui->meshStopColorButton->setColor(c);
936 d->ui->meshStopColorButton->setDisabled(false);
937 } else {
938 d->ui->meshStopColorButton->setDisabled(true);
939 }
940}
941
943{
944 if (d->noSelectionTrackingMode) return;
945
947
948 bool shouldUploadColorToResourceManager = false;
949
950 // Disable the buttons if there aren't any selected shapes or we have a several shapes with different
951 // gradient backgrounds.
952 if (shapes.isEmpty() ||
953 (shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) {
954
955 Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
956 button->setEnabled(!shapes.isEmpty());
957 }
958 } else {
959 // only one vector object selected
960 Q_FOREACH (QAbstractButton *button, d->group->buttons()) {
961 button->setEnabled(true);
962 }
963
964 // update active index of shape
965 KoShape *shape = shapes.first();
966 updateUiFromFillType(shape); // updates tool options fields
967
968 shouldUploadColorToResourceManager = true;
969 }
970
971 // updates the UI
972 d->group->button(d->selectedFillIndex)->setChecked(true);
973
976
977 if (shouldUploadColorToResourceManager) {
979 } else {
981 }
982}
983
985{
987 KoShapeFillWrapper wrapper(shape, d->fillVariant);
988
989 switch (wrapper.type()) {
990 case KoFlake::None:
991 d->selectedFillIndex = KoFillConfigWidget::None;
992 break;
993 case KoFlake::Solid: {
994 d->selectedFillIndex = KoFillConfigWidget::Solid;
995 QColor color = wrapper.color();
996 if (color.alpha() > 0) {
997 d->colorAction->setCurrentColor(wrapper.color());
998 }
999 break;
1000 }
1001 case KoFlake::Gradient:
1002 d->selectedFillIndex = KoFillConfigWidget::Gradient;
1003 updateGradientUi(wrapper.gradient());
1005 break;
1006 case KoFlake::Pattern:
1007 d->selectedFillIndex = KoFillConfigWidget::Pattern;
1008 break;
1010 d->selectedFillIndex = KoFillConfigWidget::MeshGradient;
1012 break;
1013 }
1014}
1015
1016
1018{
1019 // The UI is showing/hiding things like this because the 'stacked widget' isn't very flexible
1020 // and makes it difficult to put anything underneath it without a lot empty space
1021
1022 // hide everything first
1023 d->ui->wdgGradientEditor->setVisible(false);
1024 d->ui->btnChoosePredefinedGradient->setVisible(false);
1025 d->ui->btnChooseSolidColor->setVisible(false);
1026 d->ui->typeLabel->setVisible(false);
1027 d->ui->repeatLabel->setVisible(false);
1028 d->ui->cmbGradientRepeat->setVisible(false);
1029 d->ui->cmbGradientType->setVisible(false);
1030 d->ui->btnSolidColorSample->setVisible(false);
1031 d->ui->btnSaveGradient->setVisible(false);
1032 d->ui->gradientTypeLine->setVisible(false);
1033 d->ui->soldStrokeColorLabel->setVisible(false);
1034 d->ui->presetLabel->setVisible(false);
1035 d->ui->stopColorLabel->setVisible(false);
1036 d->ui->meshStopColorButton->setVisible(false);
1037 d->ui->rowsLabel->setVisible(false);
1038 d->ui->spinbRows->setVisible(false);
1039 d->ui->columnsLabel->setVisible(false);
1040 d->ui->spinbColumns->setVisible(false);
1041 d->ui->smoothingTypeLabel->setVisible(false);
1042 d->ui->cmbSmoothingType->setVisible(false);
1043
1044 // keep options hidden if no vector shapes are selected
1045 if(currentShapes().isEmpty()) {
1046 return;
1047 }
1048
1049
1050 switch (d->selectedFillIndex) {
1052 break;
1054 d->ui->btnChooseSolidColor->setVisible(true);
1055 d->ui->btnSolidColorSample->setVisible(false);
1056 d->ui->soldStrokeColorLabel->setVisible(true);
1057 break;
1059 d->ui->wdgGradientEditor->setVisible(true);
1060 d->ui->btnChoosePredefinedGradient->setVisible(true);
1061 d->ui->typeLabel->setVisible(true);
1062 d->ui->repeatLabel->setVisible(true);
1063 d->ui->cmbGradientRepeat->setVisible(true);
1064 d->ui->cmbGradientType->setVisible(true);
1065 d->ui->btnSaveGradient->setVisible(true);
1066 d->ui->gradientTypeLine->setVisible(true);
1067 d->ui->presetLabel->setVisible(true);
1068 break;
1070 break;
1072 d->ui->stopColorLabel->setVisible(true);
1073 d->ui->meshStopColorButton->setVisible(true);
1074 d->ui->rowsLabel->setVisible(true);
1075 d->ui->spinbRows->setVisible(true);
1076 d->ui->columnsLabel->setVisible(true);
1077 d->ui->spinbColumns->setVisible(true);
1078 d->ui->smoothingTypeLabel->setVisible(true);
1079 d->ui->cmbSmoothingType->setVisible(true);
1080 d->ui->meshStopColorButton->setAlphaChannelEnabled(true);
1081 break;
1082 }
1083
1084}
float value(const T *src, size_t ch)
const quint8 OPACITY_OPAQUE_U8
static const char *const buttongradient[]
static const char *const buttonsolid[]
static const char *const buttonnone[]
static const char *const buttonpattern[]
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
std::unique_lock< KisAcyclicSignalConnector > Blocker
void connectForwardVoid(QObject *sender, const char *signal, QObject *receiver, const char *method)
KisSelectedShapesProxy selectedShapesProxy
void addCommand(KUndo2Command *command) override
static bool addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation="")
KoSelection * selection() override
QPointer< KoCanvasResourceProvider > resourceManager
void colorChanged(const KoColor &color)
void setOpacity(quint8 alpha)
Definition KoColor.cpp:333
void fromQColor(const QColor &c)
Convenient function for converting from a QColor.
Definition KoColor.cpp:213
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
A widget for configuring the fill of a shape.
Private(KoFlake::FillVariant _fillVariant, KoFillConfigWidget *q)
QList< KoShape * > currentShapes()
void sigInternalRequestColorToResourceManager()
void createNewDefaultMeshGradientBackground()
QScopedPointer< SvgMeshGradient > activeMeshGradient
SvgMeshPosition meshposition
KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent)
void updateUiFromFillType(KoShape *shape)
updates the UI based on KoFlake::FillType it gets from the shape.
void slotCanvasResourceChanged(int key, const QVariant &value)
std::vector< KisAcyclicSignalConnector::Blocker > deactivationLocks
KisAcyclicSignalConnector resourceManagerAcyclicConnector
void sigMeshGradientResetted()
KisSignalCompressorWithParam< std::pair< QColor, KoFlake::FillVariant > > colorChangedCompressor
void setNoSelectionTrackingMode(bool value)
QScopedPointer< Ui_KoFillConfigWidget > ui
void colorChanged(std::pair< QColor, KoFlake::FillVariant > resource)
apply color changes to the selected shape
void sigInternalRecoverColorInResourceManager()
void slotMeshHandleColorChanged(const KoColor &c)
void createNewMeshGradientBackground()
sets the active gradient either from the shape (if present) or creates a new one
std::array< boost::optional< KoColor >, 2 > overriddenColorFromProvider
KisSignalCompressor gradientChangedCompressor
void slotMeshGradientChanged()
this won't preserve the rows and columns
KoFlake::FillVariant fillVariant
void updateGradientUi(const QGradient *gradient)
void patternChanged(QSharedPointer< KoShapeBackground > background)
the pattern of the fill changed, apply the changes
KoShapeStrokeSP createShapeStroke()
void styleButtonPressed(int buttonId)
void setSelectedMeshGradientHandle(const SvgMeshPosition &position)
KisSignalCompressor shapeChangedCompressor
KoStopGradientSP activeGradient
void slotMeshGradientShadingChanged(int index)
void slotProposeCurrentColorToResourceManager()
KoFillConfigWidget::StyleButton selectedFillIndex
A pattern shape background.
const QList< KoShape * > selectedEditableShapes() const
The undo / redo command for setting the shape background.
KoFlake::FillType type() const
KUndo2Command * applyGradientStopsOnly(const QGradient *gradient)
const SvgMeshGradient * meshgradient() const
KUndo2Command * setMeshGradient(const SvgMeshGradient *gradient, const QTransform &transform)
const QGradient * gradient() const
KUndo2Command * setLineWidth(const float &lineWidth)
KUndo2Command * setColor(const QColor &color)
static QSharedPointer< KoStopGradient > fromQGradient(const QGradient *gradient)
Creates KoStopGradient from a QGradient.
int numRows() const
int numColumns() const
void setType(Shading type)
const QScopedPointer< SvgMeshArray > & getMeshArray() const
void setGradientUnits(KoFlake::CoordinateSystem units=KoFlake::UserSpaceOnUse)
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
QString button(const QWheelEvent &ev)
QIcon loadIcon(const QString &name)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
@ Background
the background / fill style is active
Definition KoFlake.h:81
@ None
Definition KoFlake.h:34
@ Pattern
Definition KoFlake.h:37
@ Solid
Definition KoFlake.h:35
@ MeshGradient
Definition KoFlake.h:38
@ Gradient
Definition KoFlake.h:36
FillVariant
Definition KoFlake.h:28
@ StrokeFill
Definition KoFlake.h:30
@ Fill
Definition KoFlake.h:29
const QString Gradients
static KoResourceServerProvider * instance()
KoResourceServer< KoAbstractGradient > * gradientServer
KisCanvas2 * canvas