File indexing completed on 2024-12-22 04:13:18
0001 /* This file is part of the KDE project 0002 * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) 0003 * SPDX-FileCopyrightText: 2012 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "KoFillConfigWidget.h" 0009 0010 #include <QToolButton> 0011 #include <QHBoxLayout> 0012 #include <QVBoxLayout> 0013 #include <QButtonGroup> 0014 #include <QLabel> 0015 #include <QSizePolicy> 0016 #include <QBitmap> 0017 #include <QAction> 0018 #include <QSharedPointer> 0019 #include <QMessageBox> 0020 0021 #include <klocalizedstring.h> 0022 0023 #include <KoIcon.h> 0024 #include <KoColor.h> 0025 #include <KoColorPopupAction.h> 0026 #include "KoResourceServerProvider.h" 0027 #include <KoSelection.h> 0028 #include <KoCanvasBase.h> 0029 #include <KoCanvasResourceProvider.h> 0030 #include <KoDocumentResourceManager.h> 0031 #include <KoShape.h> 0032 #include <KoShapeController.h> 0033 #include <KoShapeBackground.h> 0034 #include <KoShapeBackgroundCommand.h> 0035 #include <KoShapeStrokeCommand.h> 0036 #include <KoShapeStroke.h> 0037 #include <KoSelectedShapesProxy.h> 0038 #include <KoColorBackground.h> 0039 #include <KoGradientBackground.h> 0040 #include <KoPatternBackground.h> 0041 #include <KoResourcePopupAction.h> 0042 #include "KoZoomHandler.h" 0043 #include "KoColorPopupButton.h" 0044 #include "ui_KoFillConfigWidget.h" 0045 #include <kis_signals_blocker.h> 0046 #include <kis_signal_compressor_with_param.h> 0047 #include <kis_acyclic_signal_connector.h> 0048 #include <kis_assert.h> 0049 #include "kis_canvas_resource_provider.h" 0050 #include <KoStopGradient.h> 0051 #include <QInputDialog> 0052 #include <KoShapeFillWrapper.h> 0053 #include <functional> 0054 0055 #include <KisResourceUserOperations.h> 0056 0057 #include "kis_global.h" 0058 #include "kis_debug.h" 0059 0060 static const char* const buttonnone[]={ 0061 "16 16 3 1", 0062 "# c #000000", 0063 "e c #ff0000", 0064 "- c #ffffff", 0065 "################", 0066 "#--------------#", 0067 "#-e----------e-#", 0068 "#--e--------e--#", 0069 "#---e------e---#", 0070 "#----e----e----#", 0071 "#-----e--e-----#", 0072 "#------ee------#", 0073 "#------ee------#", 0074 "#-----e--e-----#", 0075 "#----e----e----#", 0076 "#---e------e---#", 0077 "#--e--------e--#", 0078 "#-e----------e-#", 0079 "#--------------#", 0080 "################"}; 0081 0082 static const char* const buttonsolid[]={ 0083 "16 16 2 1", 0084 "# c #000000", 0085 ". c #969696", 0086 "################", 0087 "#..............#", 0088 "#..............#", 0089 "#..............#", 0090 "#..............#", 0091 "#..............#", 0092 "#..............#", 0093 "#..............#", 0094 "#..............#", 0095 "#..............#", 0096 "#..............#", 0097 "#..............#", 0098 "#..............#", 0099 "#..............#", 0100 "#..............#", 0101 "################"}; 0102 0103 0104 // FIXME: Smoother gradient button. 0105 0106 static const char* const buttongradient[]={ 0107 "16 16 15 1", 0108 "# c #000000", 0109 "n c #101010", 0110 "m c #202020", 0111 "l c #303030", 0112 "k c #404040", 0113 "j c #505050", 0114 "i c #606060", 0115 "h c #707070", 0116 "g c #808080", 0117 "f c #909090", 0118 "e c #a0a0a0", 0119 "d c #b0b0b0", 0120 "c c #c0c0c0", 0121 "b c #d0d0d0", 0122 "a c #e0e0e0", 0123 "################", 0124 "#abcdefghijklmn#", 0125 "#abcdefghijklmn#", 0126 "#abcdefghijklmn#", 0127 "#abcdefghijklmn#", 0128 "#abcdefghijklmn#", 0129 "#abcdefghijklmn#", 0130 "#abcdefghijklmn#", 0131 "#abcdefghijklmn#", 0132 "#abcdefghijklmn#", 0133 "#abcdefghijklmn#", 0134 "#abcdefghijklmn#", 0135 "#abcdefghijklmn#", 0136 "#abcdefghijklmn#", 0137 "#abcdefghijklmn#", 0138 "################"}; 0139 0140 static const char* const buttonpattern[]={ 0141 "16 16 4 1", 0142 ". c #0a0a0a", 0143 "# c #333333", 0144 "a c #a0a0a0", 0145 "b c #ffffffff", 0146 "################", 0147 "#aaaaabbbbaaaaa#", 0148 "#aaaaabbbbaaaaa#", 0149 "#aaaaabbbbaaaaa#", 0150 "#aaaaabbbbaaaaa#", 0151 "#aaaaabbbbaaaaa#", 0152 "#bbbbbaaaabbbbb#", 0153 "#bbbbbaaaabbbbb#", 0154 "#bbbbbaaaabbbbb#", 0155 "#bbbbbaaaabbbbb#", 0156 "#aaaaabbbbaaaaa#", 0157 "#aaaaabbbbaaaaa#", 0158 "#aaaaabbbbaaaaa#", 0159 "#aaaaabbbbaaaaa#", 0160 "#aaaaabbbbaaaaa#", 0161 "################"}; 0162 0163 using namespace std::placeholders; 0164 0165 0166 class Q_DECL_HIDDEN KoFillConfigWidget::Private 0167 { 0168 public: 0169 Private(KoFlake::FillVariant _fillVariant, KoFillConfigWidget* q) 0170 : canvas(0), 0171 colorChangedCompressor(100, std::bind(&KoFillConfigWidget::colorChanged, q, _1)), 0172 gradientChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE), 0173 shapeChangedCompressor(200,KisSignalCompressor::FIRST_ACTIVE), 0174 fillVariant(_fillVariant), 0175 noSelectionTrackingMode(false) 0176 { 0177 } 0178 0179 KoColorPopupAction *colorAction {nullptr}; 0180 KoResourcePopupAction *gradientAction {nullptr}; 0181 KoResourcePopupAction *patternAction {nullptr}; 0182 QButtonGroup *group {nullptr}; 0183 0184 KoCanvasBase *canvas {nullptr}; 0185 0186 KisSignalCompressorWithParam<std::pair<QColor, KoFlake::FillVariant>> colorChangedCompressor; 0187 KisAcyclicSignalConnector resourceManagerAcyclicConnector; 0188 KoFillConfigWidget::StyleButton selectedFillIndex {KoFillConfigWidget::None}; 0189 0190 KoStopGradientSP activeGradient; 0191 KisSignalCompressor gradientChangedCompressor; 0192 KisSignalCompressor shapeChangedCompressor; 0193 KoFlake::FillVariant fillVariant; 0194 0195 bool noSelectionTrackingMode {false}; 0196 0197 SvgMeshPosition meshposition; 0198 QScopedPointer<SvgMeshGradient> activeMeshGradient; 0199 0200 QScopedPointer<Ui_KoFillConfigWidget> ui; 0201 0202 std::vector<KisAcyclicSignalConnector::Blocker> deactivationLocks; 0203 0204 std::array<boost::optional<KoColor>, 2> overriddenColorFromProvider; 0205 }; 0206 0207 KoFillConfigWidget::KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent) 0208 : QWidget(parent) 0209 , d(new Private(fillVariant, this)) 0210 { 0211 d->canvas = canvas; 0212 0213 if (trackShapeSelection) { 0214 connect(d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), &d->shapeChangedCompressor, 0215 SLOT(start())); 0216 connect(&d->shapeChangedCompressor, SIGNAL(timeout()), this, SLOT(shapeChanged())); 0217 } 0218 0219 d->resourceManagerAcyclicConnector.connectBackwardResourcePair( 0220 d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), 0221 this, SLOT(slotCanvasResourceChanged(int,QVariant))); 0222 0223 d->resourceManagerAcyclicConnector.connectForwardVoid( 0224 this, SIGNAL(sigInternalRequestColorToResourceManager()), 0225 this, SLOT(slotProposeCurrentColorToResourceManager())); 0226 0227 KisAcyclicSignalConnector *resetConnector = d->resourceManagerAcyclicConnector.createCoordinatedConnector(); 0228 resetConnector->connectForwardVoid( 0229 this, SIGNAL(sigInternalRecoverColorInResourceManager()), 0230 this, SLOT(slotRecoverColorInResourceManager())); 0231 0232 // configure GUI 0233 0234 d->ui.reset(new Ui_KoFillConfigWidget()); 0235 d->ui->setupUi(this); 0236 0237 d->group = new QButtonGroup(this); 0238 d->group->setExclusive(true); 0239 0240 d->ui->btnNoFill->setIcon(QPixmap((const char **) buttonnone)); 0241 d->group->addButton(d->ui->btnNoFill, None); 0242 0243 d->ui->btnSolidFill->setIcon(QPixmap((const char **) buttonsolid)); 0244 d->group->addButton(d->ui->btnSolidFill, Solid); 0245 0246 d->ui->btnGradientFill->setIcon(QPixmap((const char **) buttongradient)); 0247 d->group->addButton(d->ui->btnGradientFill, Gradient); 0248 0249 d->ui->btnPatternFill->setIcon(QPixmap((const char **) buttonpattern)); 0250 d->group->addButton(d->ui->btnPatternFill, Pattern); 0251 d->ui->btnPatternFill->setVisible(false); 0252 0253 if (fillVariant == KoFlake::Fill) { 0254 // FIXME: different button 0255 d->ui->btnMeshFill->setIcon(QPixmap((const char**) buttonpattern)); 0256 d->group->addButton(d->ui->btnMeshFill, MeshGradient); 0257 } else { 0258 d->ui->btnMeshFill->setVisible(false); 0259 } 0260 0261 d->colorAction = new KoColorPopupAction(d->ui->btnChooseSolidColor); 0262 d->colorAction->setToolTip(i18n("Change the filling color")); 0263 d->colorAction->setCurrentColor(Qt::white); 0264 0265 d->ui->btnChooseSolidColor->setDefaultAction(d->colorAction); 0266 d->ui->btnChooseSolidColor->setPopupMode(QToolButton::InstantPopup); 0267 d->ui->btnSolidColorSample->setIcon(KisIconUtils::loadIcon("krita_tool_color_sampler")); 0268 0269 // TODO: for now the color sampling button is disabled! 0270 d->ui->btnSolidColorSample->setEnabled(false); 0271 d->ui->btnSolidColorSample->setVisible(false); 0272 0273 connect(d->colorAction, &KoColorPopupAction::colorChanged, [this](KoColor color) { 0274 d->colorChangedCompressor.start({color.toQColor(), d->fillVariant}); 0275 }); 0276 0277 connect(d->ui->btnChooseSolidColor, SIGNAL(iconSizeChanged()), d->colorAction, SLOT(updateIcon())); 0278 0279 connect(d->group, SIGNAL(buttonClicked(int)), SLOT(styleButtonPressed(int))); 0280 0281 connect(d->group, SIGNAL(buttonClicked(int)), SLOT(slotUpdateFillTitle())); 0282 0283 slotUpdateFillTitle(); 0284 styleButtonPressed(d->group->checkedId()); 0285 0286 0287 // Gradient selector 0288 d->ui->wdgGradientEditor->setCompactMode(true); 0289 d->ui->wdgGradientEditor->setCanvasResourcesInterface(canvas->resourceManager()->canvasResourcesInterface()); 0290 connect(d->ui->wdgGradientEditor, SIGNAL(sigGradientChanged()), &d->gradientChangedCompressor, SLOT(start())); 0291 connect(&d->gradientChangedCompressor, SIGNAL(timeout()), SLOT(activeGradientChanged())); 0292 0293 d->gradientAction = new KoResourcePopupAction(ResourceType::Gradients, canvas->resourceManager()->canvasResourcesInterface(), d->ui->btnChoosePredefinedGradient); 0294 0295 d->gradientAction->setToolTip(i18n("Change filling gradient")); 0296 d->ui->btnChoosePredefinedGradient->setDefaultAction(d->gradientAction); 0297 d->ui->btnChoosePredefinedGradient->setPopupMode(QToolButton::InstantPopup); 0298 0299 connect(d->gradientAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), 0300 SLOT(gradientResourceChanged())); 0301 connect(d->ui->btnChoosePredefinedGradient, SIGNAL(iconSizeChanged()), d->gradientAction, SLOT(updateIcon())); 0302 0303 d->ui->btnSaveGradient->setIcon(KisIconUtils::loadIcon("document-save")); 0304 connect(d->ui->btnSaveGradient, SIGNAL(clicked()), SLOT(slotSavePredefinedGradientClicked())); 0305 0306 connect(d->ui->cmbGradientRepeat, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientRepeatChanged())); 0307 connect(d->ui->cmbGradientType, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientTypeChanged())); 0308 0309 // meshgradient 0310 connect(d->ui->meshStopColorButton, SIGNAL(changed(const KoColor&)), this, SLOT(slotMeshHandleColorChanged(const KoColor&))); 0311 0312 d->ui->spinbRows->setRange(1, 20); 0313 d->ui->spinbColumns->setRange(1, 20); 0314 connect(d->ui->spinbRows, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged())); 0315 connect(d->ui->spinbColumns, SIGNAL(valueChanged(int)), SLOT(slotMeshGradientChanged())); 0316 connect(d->ui->cmbSmoothingType, SIGNAL(currentIndexChanged(int)), SLOT(slotMeshGradientShadingChanged(int))); 0317 0318 // initialize deactivation locks 0319 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); 0320 0321 0322 /* 0323 // Pattern selector 0324 d->patternAction = new KoResourcePopupAction(ResourceType::Patterns, d->colorButton); 0325 d->patternAction->setToolTip(i18n("Change the filling pattern")); 0326 connect(d->patternAction, SIGNAL(resourceSelected(QSharedPointer<KoShapeBackground>)), this, SLOT(patternChanged(QSharedPointer<KoShapeBackground>))); 0327 connect(d->colorButton, SIGNAL(iconSizeChanged()), d->patternAction, SLOT(updateIcon())); 0328 */ 0329 0330 0331 } 0332 0333 KoFillConfigWidget::~KoFillConfigWidget() 0334 { 0335 delete d; 0336 } 0337 0338 void KoFillConfigWidget::activate() 0339 { 0340 KIS_SAFE_ASSERT_RECOVER_NOOP(!d->deactivationLocks.empty()); 0341 d->deactivationLocks.clear(); 0342 0343 if (!d->noSelectionTrackingMode) { 0344 d->shapeChangedCompressor.start(); 0345 } else { 0346 loadCurrentFillFromResourceServer(); 0347 } 0348 0349 updateWidgetComponentVisibility(); 0350 } 0351 0352 void KoFillConfigWidget::deactivate() 0353 { 0354 emit sigInternalRecoverColorInResourceManager(); 0355 0356 KIS_SAFE_ASSERT_RECOVER_NOOP(d->deactivationLocks.empty()); 0357 d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); 0358 } 0359 0360 void KoFillConfigWidget::forceUpdateOnSelectionChanged() 0361 { 0362 d->shapeChangedCompressor.start(); 0363 } 0364 0365 void KoFillConfigWidget::setSelectedMeshGradientHandle(const SvgMeshPosition &position) 0366 { 0367 d->meshposition = position; 0368 updateMeshGradientUI(); 0369 } 0370 0371 void KoFillConfigWidget::setNoSelectionTrackingMode(bool value) 0372 { 0373 d->noSelectionTrackingMode = value; 0374 if (!d->noSelectionTrackingMode) { 0375 d->shapeChangedCompressor.start(); 0376 } 0377 } 0378 0379 void KoFillConfigWidget::slotUpdateFillTitle() 0380 { 0381 QString text = d->group->checkedButton() ? d->group->checkedButton()->text() : QString(); 0382 text.replace('&', QString()); 0383 d->ui->lblFillTitle->setText(text); 0384 } 0385 0386 void KoFillConfigWidget::slotCanvasResourceChanged(int key, const QVariant &value) 0387 { 0388 if (key == KoCanvasResource::ForegroundColor 0389 || key == KoCanvasResource::BackgroundColor) { 0390 0391 KoColor color = value.value<KoColor>(); 0392 0393 const int checkedId = d->group->checkedId(); 0394 0395 if (checkedId < 0 || checkedId == None || checkedId == Solid) { 0396 0397 d->group->button(Solid)->setChecked(true); 0398 d->selectedFillIndex = Solid; 0399 0400 KoFlake::FillVariant colorSlot = (key == KoCanvasResource::ForegroundColor) 0401 ? KoFlake::Fill 0402 : KoFlake::StrokeFill; 0403 0404 if (key == d->fillVariant) { 0405 d->colorAction->setCurrentColor(color); 0406 } 0407 colorChanged({color.toQColor(), colorSlot}); 0408 } else if (checkedId == Gradient && key == KoCanvasResource::ForegroundColor) { 0409 d->ui->wdgGradientEditor->notifyGlobalColorChanged(color); 0410 } 0411 } else if (key == KoCanvasResource::CurrentGradient) { 0412 KoResourceSP gradient = value.value<KoAbstractGradientSP>(); 0413 const int checkedId = d->group->checkedId(); 0414 0415 if (gradient && (checkedId < 0 || checkedId == None || checkedId == Gradient)) { 0416 d->group->button(Gradient)->setChecked(true); 0417 d->gradientAction->setCurrentResource(gradient); 0418 } 0419 } 0420 } 0421 0422 QList<KoShape*> KoFillConfigWidget::currentShapes() 0423 { 0424 return d->canvas->selectedShapesProxy()->selection()->selectedEditableShapes(); 0425 } 0426 0427 int KoFillConfigWidget::selectedFillIndex() { 0428 return d->selectedFillIndex; 0429 } 0430 0431 void KoFillConfigWidget::styleButtonPressed(int buttonId) 0432 { 0433 QList<KoShape*> shapes = currentShapes(); 0434 0435 switch (buttonId) { 0436 case KoFillConfigWidget::None: 0437 noColorSelected(); 0438 break; 0439 case KoFillConfigWidget::Solid: 0440 colorChanged({d->colorAction->currentColor(), d->fillVariant}); 0441 break; 0442 case KoFillConfigWidget::Gradient: 0443 if (d->activeGradient) { 0444 setNewGradientBackgroundToShape(); 0445 updateGradientSaveButtonAvailability(); 0446 } else { 0447 gradientResourceChanged(); 0448 } 0449 break; 0450 case KoFillConfigWidget::Pattern: 0451 // Only select mode in the widget, don't set actual pattern :/ 0452 //d->colorButton->setDefaultAction(d->patternAction); 0453 //patternChanged(d->patternAction->currentBackground()); 0454 break; 0455 case KoFillConfigWidget::MeshGradient: 0456 if (d->activeMeshGradient) { 0457 setNewMeshGradientBackgroundToShape(); 0458 } else { 0459 createNewMeshGradientBackground(); 0460 setNewMeshGradientBackgroundToShape(); 0461 } 0462 break; 0463 } 0464 0465 0466 // update tool option fields with first selected object 0467 if (shapes.isEmpty() == false) { 0468 KoShape *firstShape = shapes.first(); 0469 updateUiFromFillType(firstShape); 0470 } 0471 0472 updateWidgetComponentVisibility(); 0473 } 0474 0475 KoShapeStrokeSP KoFillConfigWidget::createShapeStroke() 0476 { 0477 KoShapeStrokeSP stroke(new KoShapeStroke()); 0478 KIS_ASSERT_RECOVER_RETURN_VALUE(d->fillVariant == KoFlake::StrokeFill, stroke); 0479 0480 switch (d->group->checkedId()) { 0481 case KoFillConfigWidget::None: 0482 stroke->setColor(Qt::transparent); 0483 break; 0484 case KoFillConfigWidget::Solid: 0485 stroke->setColor(d->colorAction->currentColor()); 0486 break; 0487 case KoFillConfigWidget::Gradient: { 0488 QScopedPointer<QGradient> g(d->activeGradient->toQGradient()); 0489 QBrush newBrush = *g; 0490 stroke->setLineBrush(newBrush); 0491 stroke->setColor(Qt::transparent); 0492 break; 0493 } 0494 case KoFillConfigWidget::Pattern: 0495 break; 0496 } 0497 0498 return stroke; 0499 } 0500 0501 void KoFillConfigWidget::noColorSelected() 0502 { 0503 QList<KoShape*> selectedShapes = currentShapes(); 0504 if (selectedShapes.isEmpty()) { 0505 emit sigFillChanged(); 0506 return; 0507 } 0508 0509 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); 0510 KUndo2Command *command = wrapper.setColor(QColor()); 0511 0512 if (command) { 0513 d->canvas->addCommand(command); 0514 } 0515 0516 emit sigFillChanged(); 0517 } 0518 0519 void KoFillConfigWidget::colorChanged(std::pair<QColor, KoFlake::FillVariant> resource) 0520 { 0521 QColor color = resource.first; 0522 KoFlake::FillVariant fillVariant = resource.second; 0523 if (!color.isValid()) { 0524 return; 0525 } 0526 0527 QList<KoShape*> selectedShapes = currentShapes(); 0528 if (selectedShapes.isEmpty()) { 0529 emit sigInternalRequestColorToResourceManager(); 0530 emit sigFillChanged(); 0531 return; 0532 } 0533 0534 d->overriddenColorFromProvider[fillVariant] = boost::none; 0535 0536 KoShapeFillWrapper wrapper(selectedShapes, fillVariant); 0537 0538 KUndo2Command *command = wrapper.setColor(color); 0539 if (command) { 0540 d->canvas->addCommand(command); 0541 } 0542 0543 // only returns true if it is a stroke object that has a 0 for line width 0544 if (wrapper.hasZeroLineWidth() ) { 0545 KUndo2Command *lineCommand = wrapper.setLineWidth(1.0); 0546 if (lineCommand) { 0547 d->canvas->addCommand(lineCommand); 0548 } 0549 0550 // * line to test out 0551 QColor solidColor = d->colorAction->currentColor(); 0552 solidColor.setAlpha(255); 0553 command = wrapper.setColor(solidColor); 0554 if (command) { 0555 d->canvas->addCommand(command); 0556 } 0557 0558 } 0559 0560 emit sigFillChanged(); 0561 emit sigInternalRequestColorToResourceManager(); 0562 } 0563 0564 void KoFillConfigWidget::slotProposeCurrentColorToResourceManager() 0565 { 0566 const int checkedId = d->group->checkedId(); 0567 0568 auto uploadColorToResourceManager = [this](KoCanvasResource::CanvasResourceId res, 0569 KoFlake::FillVariant var, KoColor &color) { 0570 if (!d->overriddenColorFromProvider[var]) { 0571 d->overriddenColorFromProvider[var] = 0572 d->canvas->resourceManager()->resource(res).value<KoColor>(); 0573 } 0574 0575 /** 0576 * Don't let opacity leak to our resource manager system 0577 * 0578 * NOTE: theoretically, we could guarantee it on a level of the 0579 * resource manager itself, 0580 */ 0581 color.setOpacity(OPACITY_OPAQUE_U8); 0582 d->canvas->resourceManager()->setResource(res, QVariant::fromValue(color)); 0583 }; 0584 0585 auto uploadColorFromShape = [&](KoCanvasResource::CanvasResourceId res, KoFlake::FillVariant fill) { 0586 KoShapeFillWrapper wrapper(currentShapes(), fill); 0587 if (!wrapper.color().isValid()) { 0588 return; 0589 } 0590 KoColor color; 0591 color.fromQColor(wrapper.color()); 0592 uploadColorToResourceManager(res, fill, color); 0593 }; 0594 0595 if (checkedId == Solid) { 0596 if (currentShapes().isEmpty()) { 0597 KoCanvasResource::CanvasResourceId res = 0598 (d->fillVariant == KoFlake::Fill) ? KoCanvasResource::ForegroundColor 0599 : KoCanvasResource::BackgroundColor; 0600 KoColor color = d->colorAction->currentKoColor(); 0601 uploadColorToResourceManager(res, d->fillVariant, color); 0602 } else { 0603 uploadColorFromShape(KoCanvasResource::BackgroundColor, KoFlake::StrokeFill); 0604 uploadColorFromShape(KoCanvasResource::ForegroundColor, KoFlake::Fill); 0605 } 0606 } else if (checkedId == Gradient) { 0607 if (boost::optional<KoColor> gradientColor = 0608 d->ui->wdgGradientEditor->currentActiveStopColor()) { 0609 KoColor color = *gradientColor; 0610 KoCanvasResource::CanvasResourceId res = 0611 (d->fillVariant == KoFlake::Fill) ? KoCanvasResource::ForegroundColor 0612 : KoCanvasResource::BackgroundColor; 0613 uploadColorToResourceManager(res, d->fillVariant, color); 0614 } 0615 } 0616 } 0617 0618 void KoFillConfigWidget::slotRecoverColorInResourceManager() 0619 { 0620 auto checkAndRecover = [this](KoCanvasResource::CanvasResourceId res, 0621 KoFlake::FillVariant var) { 0622 if (d->overriddenColorFromProvider[var]) { 0623 d->canvas->resourceManager()->setResource( 0624 res, QVariant::fromValue(*d->overriddenColorFromProvider[var])); 0625 d->overriddenColorFromProvider[var] = boost::none; 0626 } 0627 }; 0628 0629 checkAndRecover(KoCanvasResource::BackgroundColor, KoFlake::StrokeFill); 0630 checkAndRecover(KoCanvasResource::ForegroundColor, KoFlake::Fill); 0631 } 0632 0633 void KoFillConfigWidget::slotSavePredefinedGradientClicked() 0634 { 0635 KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance(); 0636 auto server = serverProvider->gradientServer(); 0637 0638 const QString defaultGradientNamePrefix = i18nc("default prefix for the saved gradient", "gradient"); 0639 const QString saveLocation = server->saveLocation(); 0640 0641 QString name = d->activeGradient->name().isEmpty() ? defaultGradientNamePrefix : d->activeGradient->name(); 0642 QFileInfo fileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension()); 0643 bool fileOverWriteAccepted = false; 0644 0645 while(!fileOverWriteAccepted) { 0646 name = QInputDialog::getText(this, 0647 i18nc("@title:window", "Save Gradient"), 0648 i18n("Enter gradient name:"), 0649 QLineEdit::Normal, name); 0650 if (name.isNull() || name.isEmpty()) { 0651 return; 0652 } else { 0653 fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + d->activeGradient->defaultFileExtension()); 0654 if (fileInfo.exists()) { 0655 int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists") 0656 , i18n("The name '%1' already exists, do you wish to overwrite it?", name) 0657 , QMessageBox::Yes | QMessageBox::No); 0658 if (res == QMessageBox::Yes) fileOverWriteAccepted = true; 0659 } else { 0660 fileOverWriteAccepted = true; 0661 } 0662 } 0663 } 0664 0665 d->activeGradient->setName(name); 0666 d->activeGradient->setFilename(name.split(" ").join("_") + d->activeGradient->defaultFileExtension()); 0667 0668 KoAbstractGradientSP newGradient = d->activeGradient->clone().dynamicCast<KoAbstractGradient>(); 0669 0670 KisResourceUserOperations::addResourceWithUserInput(this, newGradient); 0671 0672 d->gradientAction->setCurrentResource(newGradient); 0673 } 0674 0675 void KoFillConfigWidget::activeGradientChanged() 0676 { 0677 setNewGradientBackgroundToShape(); 0678 updateGradientSaveButtonAvailability(); 0679 0680 emit sigInternalRequestColorToResourceManager(); 0681 } 0682 0683 void KoFillConfigWidget::gradientResourceChanged() 0684 { 0685 QSharedPointer<KoGradientBackground> bg = 0686 qSharedPointerDynamicCast<KoGradientBackground>( 0687 d->gradientAction->currentBackground()); 0688 0689 updateGradientUi(bg->gradient()); 0690 0691 setNewGradientBackgroundToShape(); 0692 updateGradientSaveButtonAvailability(); 0693 } 0694 0695 void KoFillConfigWidget::slotGradientTypeChanged() 0696 { 0697 QGradient::Type type = 0698 d->ui->cmbGradientType->currentIndex() == 0 ? 0699 QGradient::LinearGradient : QGradient::RadialGradient; 0700 0701 d->activeGradient->setType(type); 0702 activeGradientChanged(); 0703 } 0704 0705 void KoFillConfigWidget::slotGradientRepeatChanged() 0706 { 0707 QGradient::Spread spread = 0708 QGradient::Spread(d->ui->cmbGradientRepeat->currentIndex()); 0709 0710 d->activeGradient->setSpread(spread); 0711 activeGradientChanged(); 0712 } 0713 0714 void KoFillConfigWidget::updateGradientUi(const QGradient *gradient) 0715 { 0716 KisSignalsBlocker b1(d->ui->wdgGradientEditor, 0717 d->ui->cmbGradientType, 0718 d->ui->cmbGradientRepeat); 0719 0720 d->ui->wdgGradientEditor->setGradient(0); 0721 0722 d->activeGradient = KoStopGradient::fromQGradient(gradient); 0723 0724 d->ui->wdgGradientEditor->setGradient(d->activeGradient); 0725 d->ui->cmbGradientType->setCurrentIndex(d->activeGradient->type() != QGradient::LinearGradient); 0726 d->ui->cmbGradientRepeat->setCurrentIndex(int(d->activeGradient->spread())); 0727 } 0728 0729 void KoFillConfigWidget::setNewGradientBackgroundToShape() 0730 { 0731 QList<KoShape*> selectedShapes = currentShapes(); 0732 if (selectedShapes.isEmpty()) { 0733 emit sigFillChanged(); 0734 return; 0735 } 0736 0737 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); 0738 QScopedPointer<QGradient> srcQGradient(d->activeGradient->toQGradient()); 0739 KUndo2Command *command = wrapper.applyGradientStopsOnly(srcQGradient.data()); 0740 0741 if (command) { 0742 d->canvas->addCommand(command); 0743 } 0744 0745 emit sigFillChanged(); 0746 } 0747 0748 void KoFillConfigWidget::updateGradientSaveButtonAvailability() 0749 { 0750 bool savingEnabled = false; 0751 0752 QScopedPointer<QGradient> currentGradient(d->activeGradient->toQGradient()); 0753 QSharedPointer<KoShapeBackground> bg = d->gradientAction->currentBackground(); 0754 if (bg) { 0755 QSharedPointer<KoGradientBackground> resourceBackground = 0756 qSharedPointerDynamicCast<KoGradientBackground>(bg); 0757 0758 savingEnabled = resourceBackground->gradient()->stops() != currentGradient->stops(); 0759 savingEnabled |= resourceBackground->gradient()->type() != currentGradient->type(); 0760 savingEnabled |= resourceBackground->gradient()->spread() != currentGradient->spread(); 0761 } 0762 0763 d->ui->btnSaveGradient->setEnabled(savingEnabled); 0764 } 0765 0766 void KoFillConfigWidget::patternChanged(QSharedPointer<KoShapeBackground> background) 0767 { 0768 Q_UNUSED(background); 0769 0770 #if 0 0771 QSharedPointer<KoPatternBackground> patternBackground = qSharedPointerDynamicCast<KoPatternBackground>(background); 0772 if (! patternBackground) { 0773 return; 0774 } 0775 0776 QList<KoShape*> selectedShapes = currentShapes(); 0777 if (selectedShapes.isEmpty()) { 0778 return; 0779 } 0780 0781 QSharedPointer<KoPatternBackground> fill(new KoPatternBackground()); 0782 fill->setPattern(patternBackground->pattern()); 0783 d->canvas->addCommand(new KoShapeBackgroundCommand(selectedShapes, fill)); 0784 0785 #endif 0786 } 0787 0788 void KoFillConfigWidget::slotMeshGradientChanged() 0789 { 0790 createNewDefaultMeshGradientBackground(); 0791 setNewMeshGradientBackgroundToShape(); 0792 d->meshposition = SvgMeshPosition(); 0793 emit sigMeshGradientResetted(); 0794 } 0795 0796 void KoFillConfigWidget::slotMeshGradientShadingChanged(int index) 0797 { 0798 d->activeMeshGradient->setType(static_cast<SvgMeshGradient::Shading>(index)); 0799 setNewMeshGradientBackgroundToShape(); 0800 } 0801 0802 void KoFillConfigWidget::slotMeshHandleColorChanged(const KoColor &c) 0803 { 0804 QList<KoShape*> selectedShapes = currentShapes(); 0805 KIS_SAFE_ASSERT_RECOVER_RETURN(!selectedShapes.isEmpty()); 0806 0807 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); 0808 const SvgMeshGradient *gradient = wrapper.meshgradient(); 0809 0810 // if we changed the handle, the gradient *has* to exist 0811 KIS_SAFE_ASSERT_RECOVER_RETURN(gradient); 0812 0813 if (d->meshposition.isValid()) { 0814 // We don't have any signals firing when we change the structure (i.e position of stops etc) of a 0815 // meshgradient. So, activeMeshGradient isn't updated when this happens. Hence we update it here and 0816 // then modify the color. 0817 d->activeMeshGradient.reset(new SvgMeshGradient(*gradient)); 0818 0819 d->activeMeshGradient->getMeshArray()->modifyColor(d->meshposition, c.toQColor()); 0820 setNewMeshGradientBackgroundToShape(); 0821 } 0822 return; 0823 } 0824 0825 void KoFillConfigWidget::loadCurrentFillFromResourceServer() 0826 { 0827 { 0828 KoColor color = d->canvas->resourceManager()->foregroundColor(); 0829 if (d->group->checkedId() == -1 || d->group->checkedId() == None) { 0830 d->group->button(Solid)->setChecked(true); 0831 } 0832 d->selectedFillIndex = Solid; 0833 d->colorAction->setCurrentColor(color); 0834 } 0835 0836 Q_FOREACH (QAbstractButton *button, d->group->buttons()) { 0837 button->setEnabled(true); 0838 } 0839 0840 emit sigFillChanged(); 0841 } 0842 0843 void KoFillConfigWidget::createNewMeshGradientBackground() 0844 { 0845 QList<KoShape*> selectedShapes = currentShapes(); 0846 if (selectedShapes.isEmpty()) { 0847 return; 0848 } 0849 0850 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); 0851 const SvgMeshGradient *g = wrapper.meshgradient(); 0852 if (g) { 0853 d->activeMeshGradient.reset(new SvgMeshGradient(*g)); 0854 } else { 0855 createNewDefaultMeshGradientBackground(); 0856 } 0857 0858 updateMeshGradientUI(); 0859 } 0860 0861 void KoFillConfigWidget::createNewDefaultMeshGradientBackground() 0862 { 0863 QList<KoShape*> selectedShapes = currentShapes(); 0864 if (selectedShapes.isEmpty()) { 0865 return; 0866 } 0867 0868 // use this for mesh creation 0869 QSizeF maxSize; 0870 for (const auto& shape: selectedShapes) { 0871 QSizeF size = shape->boundingRect().size(); 0872 if (size.height() > maxSize.height()) { 0873 maxSize.rheight() = size.height(); 0874 } 0875 if (size.width() > maxSize.width()) { 0876 maxSize.rwidth() = size.width(); 0877 } 0878 } 0879 0880 SvgMeshGradient *gradient = new SvgMeshGradient; 0881 0882 QColor color = d->canvas->resourceManager()->resource(KoFlake::Background).value<KoColor>().toQColor(); 0883 0884 int nrows = d->ui->spinbRows->value(); 0885 int ncols = d->ui->spinbColumns->value(); 0886 0887 if (d->ui->cmbSmoothingType->currentIndex()) { 0888 gradient->setType(SvgMeshGradient::BICUBIC); 0889 } else { 0890 gradient->setType(SvgMeshGradient::BILINEAR); 0891 } 0892 0893 gradient->getMeshArray()->createDefaultMesh(nrows, ncols, color, maxSize); 0894 gradient->setGradientUnits(KoFlake::ObjectBoundingBox); 0895 d->activeMeshGradient.reset(gradient); 0896 } 0897 0898 void KoFillConfigWidget::setNewMeshGradientBackgroundToShape() 0899 { 0900 QList<KoShape*> selectedShapes = currentShapes(); 0901 // if called by "manager" 0902 if (selectedShapes.isEmpty()) { 0903 emit sigFillChanged(); 0904 return; 0905 } 0906 0907 KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); 0908 0909 KUndo2Command *command = wrapper.setMeshGradient(d->activeMeshGradient.data(), QTransform()); 0910 if (command) { 0911 d->canvas->addCommand(command); 0912 } 0913 0914 emit sigFillChanged(); 0915 } 0916 0917 void KoFillConfigWidget::updateMeshGradientUI() 0918 { 0919 if (!d->activeMeshGradient) return; 0920 0921 KisSignalsBlocker b(d->ui->spinbRows, 0922 d->ui->spinbColumns, 0923 d->ui->cmbSmoothingType, 0924 d->ui->meshStopColorButton); 0925 0926 SvgMeshArray *mesharray = d->activeMeshGradient->getMeshArray().data(); 0927 d->ui->spinbRows->setValue(mesharray->numRows()); 0928 d->ui->spinbColumns->setValue(mesharray->numColumns()); 0929 d->ui->cmbSmoothingType->setCurrentIndex(d->activeMeshGradient->type()); 0930 if (d->meshposition.isValid()) { 0931 QColor qc = d->activeMeshGradient->getMeshArray()->getStop(d->meshposition).color; 0932 0933 KoColor c = d->ui->meshStopColorButton->color(); 0934 c.fromQColor(qc); 0935 0936 d->ui->meshStopColorButton->setColor(c); 0937 d->ui->meshStopColorButton->setDisabled(false); 0938 } else { 0939 d->ui->meshStopColorButton->setDisabled(true); 0940 } 0941 } 0942 0943 void KoFillConfigWidget::shapeChanged() 0944 { 0945 if (d->noSelectionTrackingMode) return; 0946 0947 QList<KoShape*> shapes = currentShapes(); 0948 0949 bool shouldUploadColorToResourceManager = false; 0950 0951 // Disable the buttons if there aren't any selected shapes or we have a several shapes with different 0952 // gradient backgrounds. 0953 if (shapes.isEmpty() || 0954 (shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) { 0955 0956 Q_FOREACH (QAbstractButton *button, d->group->buttons()) { 0957 button->setEnabled(!shapes.isEmpty()); 0958 } 0959 } else { 0960 // only one vector object selected 0961 Q_FOREACH (QAbstractButton *button, d->group->buttons()) { 0962 button->setEnabled(true); 0963 } 0964 0965 // update active index of shape 0966 KoShape *shape = shapes.first(); 0967 updateUiFromFillType(shape); // updates tool options fields 0968 0969 shouldUploadColorToResourceManager = true; 0970 } 0971 0972 // updates the UI 0973 d->group->button(d->selectedFillIndex)->setChecked(true); 0974 0975 updateWidgetComponentVisibility(); 0976 slotUpdateFillTitle(); 0977 0978 if (shouldUploadColorToResourceManager) { 0979 emit sigInternalRequestColorToResourceManager(); 0980 } else { 0981 emit sigInternalRecoverColorInResourceManager(); 0982 } 0983 } 0984 0985 void KoFillConfigWidget::updateUiFromFillType(KoShape *shape) 0986 { 0987 KIS_SAFE_ASSERT_RECOVER_RETURN(shape); 0988 KoShapeFillWrapper wrapper(shape, d->fillVariant); 0989 0990 switch (wrapper.type()) { 0991 case KoFlake::None: 0992 d->selectedFillIndex = KoFillConfigWidget::None; 0993 break; 0994 case KoFlake::Solid: { 0995 d->selectedFillIndex = KoFillConfigWidget::Solid; 0996 QColor color = wrapper.color(); 0997 if (color.alpha() > 0) { 0998 d->colorAction->setCurrentColor(wrapper.color()); 0999 } 1000 break; 1001 } 1002 case KoFlake::Gradient: 1003 d->selectedFillIndex = KoFillConfigWidget::Gradient; 1004 updateGradientUi(wrapper.gradient()); 1005 updateGradientSaveButtonAvailability(); 1006 break; 1007 case KoFlake::Pattern: 1008 d->selectedFillIndex = KoFillConfigWidget::Pattern; 1009 break; 1010 case KoFlake::MeshGradient: 1011 d->selectedFillIndex = KoFillConfigWidget::MeshGradient; 1012 createNewMeshGradientBackground(); 1013 break; 1014 } 1015 } 1016 1017 1018 void KoFillConfigWidget::updateWidgetComponentVisibility() 1019 { 1020 // The UI is showing/hiding things like this because the 'stacked widget' isn't very flexible 1021 // and makes it difficult to put anything underneath it without a lot empty space 1022 1023 // hide everything first 1024 d->ui->wdgGradientEditor->setVisible(false); 1025 d->ui->btnChoosePredefinedGradient->setVisible(false); 1026 d->ui->btnChooseSolidColor->setVisible(false); 1027 d->ui->typeLabel->setVisible(false); 1028 d->ui->repeatLabel->setVisible(false); 1029 d->ui->cmbGradientRepeat->setVisible(false); 1030 d->ui->cmbGradientType->setVisible(false); 1031 d->ui->btnSolidColorSample->setVisible(false); 1032 d->ui->btnSaveGradient->setVisible(false); 1033 d->ui->gradientTypeLine->setVisible(false); 1034 d->ui->soldStrokeColorLabel->setVisible(false); 1035 d->ui->presetLabel->setVisible(false); 1036 d->ui->stopColorLabel->setVisible(false); 1037 d->ui->meshStopColorButton->setVisible(false); 1038 d->ui->rowsLabel->setVisible(false); 1039 d->ui->spinbRows->setVisible(false); 1040 d->ui->columnsLabel->setVisible(false); 1041 d->ui->spinbColumns->setVisible(false); 1042 d->ui->smoothingTypeLabel->setVisible(false); 1043 d->ui->cmbSmoothingType->setVisible(false); 1044 1045 // keep options hidden if no vector shapes are selected 1046 if(currentShapes().isEmpty()) { 1047 return; 1048 } 1049 1050 1051 switch (d->selectedFillIndex) { 1052 case KoFillConfigWidget::None: 1053 break; 1054 case KoFillConfigWidget::Solid: 1055 d->ui->btnChooseSolidColor->setVisible(true); 1056 d->ui->btnSolidColorSample->setVisible(false); 1057 d->ui->soldStrokeColorLabel->setVisible(true); 1058 break; 1059 case KoFillConfigWidget::Gradient: 1060 d->ui->wdgGradientEditor->setVisible(true); 1061 d->ui->btnChoosePredefinedGradient->setVisible(true); 1062 d->ui->typeLabel->setVisible(true); 1063 d->ui->repeatLabel->setVisible(true); 1064 d->ui->cmbGradientRepeat->setVisible(true); 1065 d->ui->cmbGradientType->setVisible(true); 1066 d->ui->btnSaveGradient->setVisible(true); 1067 d->ui->gradientTypeLine->setVisible(true); 1068 d->ui->presetLabel->setVisible(true); 1069 break; 1070 case KoFillConfigWidget::Pattern: 1071 break; 1072 case KoFillConfigWidget::MeshGradient: 1073 d->ui->stopColorLabel->setVisible(true); 1074 d->ui->meshStopColorButton->setVisible(true); 1075 d->ui->rowsLabel->setVisible(true); 1076 d->ui->spinbRows->setVisible(true); 1077 d->ui->columnsLabel->setVisible(true); 1078 d->ui->spinbColumns->setVisible(true); 1079 d->ui->smoothingTypeLabel->setVisible(true); 1080 d->ui->cmbSmoothingType->setVisible(true); 1081 d->ui->meshStopColorButton->setAlphaChannelEnabled(true); 1082 break; 1083 } 1084 1085 }