File indexing completed on 2024-12-22 04:16:49
0001 /* 0002 * KDE. Krita Project. 0003 * 0004 * SPDX-FileCopyrightText: 2022 Deif Lou <ginoba@gmail.com> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include <kis_debug.h> 0010 #include <klocalizedstring.h> 0011 0012 #include <QLabel> 0013 #include <QCheckBox> 0014 #include <QPushButton> 0015 0016 #include <KisOptionButtonStrip.h> 0017 #include <KisOptionCollectionWidget.h> 0018 #include <KoGroupButton.h> 0019 0020 #include <ksharedconfig.h> 0021 0022 #include <KoCanvasBase.h> 0023 #include <KoPointerEvent.h> 0024 0025 #include <kis_layer.h> 0026 #include <kis_painter.h> 0027 #include <resources/KoPattern.h> 0028 #include <kis_selection.h> 0029 0030 #include <KisViewManager.h> 0031 #include <canvas/kis_canvas2.h> 0032 #include <widgets/kis_cmb_composite.h> 0033 #include <kis_slider_spin_box.h> 0034 #include <kis_cursor.h> 0035 #include "kis_resources_snapshot.h" 0036 #include <kis_color_filter_combo.h> 0037 #include <KisAngleSelector.h> 0038 #include <KoGroupButton.h> 0039 #include <kis_color_button.h> 0040 #include <kis_color_label_selector_widget.h> 0041 #include <kis_cmb_composite.h> 0042 #include <kis_image_animation_interface.h> 0043 0044 #include <kis_processing_applicator.h> 0045 #include <kis_command_utils.h> 0046 #include <functional> 0047 #include <kis_group_layer.h> 0048 #include <kis_layer_utils.h> 0049 0050 #include <KisSpinBoxI18nHelper.h> 0051 #include <KisPart.h> 0052 #include <KisDocument.h> 0053 #include <kis_dummies_facade.h> 0054 #include <KoShapeControllerBase.h> 0055 #include <kis_shape_controller.h> 0056 #include <kis_canvas_resource_provider.h> 0057 0058 #include <KoCompositeOpRegistry.h> 0059 0060 #include <processing/KisEncloseAndFillProcessingVisitor.h> 0061 0062 #include "KisToolEncloseAndFill.h" 0063 #include "subtools/KisRectangleEnclosingProducer.h" 0064 #include "subtools/KisEllipseEnclosingProducer.h" 0065 #include "subtools/KisPathEnclosingProducer.h" 0066 #include "subtools/KisLassoEnclosingProducer.h" 0067 #include "subtools/KisBrushEnclosingProducer.h" 0068 0069 KisToolEncloseAndFill::KisToolEncloseAndFill(KoCanvasBase * canvas) 0070 : KisDynamicDelegatedTool<KisToolShape>(canvas, QCursor()) 0071 { 0072 setObjectName("tool_enclose_and_fill"); 0073 } 0074 0075 KisToolEncloseAndFill::~KisToolEncloseAndFill() 0076 {} 0077 0078 void KisToolEncloseAndFill::resetCursorStyle() 0079 { 0080 KisDynamicDelegatedTool::resetCursorStyle(); 0081 overrideCursorIfNotEditable(); 0082 } 0083 0084 void KisToolEncloseAndFill::activate(const QSet<KoShape*> &shapes) 0085 { 0086 KisDynamicDelegatedTool::activate(shapes); 0087 m_configGroup = KSharedConfig::openConfig()->group(toolId()); 0088 KisCanvas2 *kisCanvas = static_cast<KisCanvas2*>(canvas()); 0089 KisCanvasResourceProvider *resourceProvider = kisCanvas->viewManager()->canvasResourceProvider(); 0090 if (resourceProvider) { 0091 connect(resourceProvider, 0092 SIGNAL(sigNodeChanged(const KisNodeSP)), 0093 this, 0094 SLOT(slot_currentNodeChanged(const KisNodeSP))); 0095 slot_currentNodeChanged(currentNode()); 0096 } 0097 } 0098 0099 void KisToolEncloseAndFill::deactivate() 0100 { 0101 m_referencePaintDevice = nullptr; 0102 m_referenceNodeList = nullptr; 0103 KisCanvas2 *kisCanvas = static_cast<KisCanvas2*>(canvas()); 0104 KisCanvasResourceProvider *resourceProvider = kisCanvas->viewManager()->canvasResourceProvider(); 0105 if (resourceProvider) { 0106 disconnect(resourceProvider, 0107 SIGNAL(sigNodeChanged(const KisNodeSP)), 0108 this, 0109 SLOT(slot_currentNodeChanged(const KisNodeSP))); 0110 } 0111 slot_currentNodeChanged(nullptr); 0112 KisDynamicDelegatedTool::deactivate(); 0113 } 0114 0115 void KisToolEncloseAndFill::setupEnclosingSubtool() 0116 { 0117 if (delegateTool()) { 0118 delegateTool()->deactivate(); 0119 } 0120 0121 if (m_enclosingMethod == Ellipse) { 0122 KisEllipseEnclosingProducer *newDelegateTool = new KisEllipseEnclosingProducer(canvas()); 0123 setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool)); 0124 setCursor(newDelegateTool->cursor()); 0125 } else if (m_enclosingMethod == Path) { 0126 KisPathEnclosingProducer *newDelegateTool = new KisPathEnclosingProducer(canvas()); 0127 setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool)); 0128 setCursor(newDelegateTool->cursor()); 0129 } else if (m_enclosingMethod == Lasso) { 0130 KisLassoEnclosingProducer *newDelegateTool = new KisLassoEnclosingProducer(canvas()); 0131 setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool)); 0132 setCursor(newDelegateTool->cursor()); 0133 } else if (m_enclosingMethod == Brush) { 0134 KisBrushEnclosingProducer *newDelegateTool = new KisBrushEnclosingProducer(canvas()); 0135 setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool)); 0136 setCursor(newDelegateTool->cursor()); 0137 } else { 0138 KisRectangleEnclosingProducer *newDelegateTool = new KisRectangleEnclosingProducer(canvas()); 0139 setDelegateTool(reinterpret_cast<KisDynamicDelegateTool<KisToolShape>*>(newDelegateTool)); 0140 setCursor(newDelegateTool->cursor()); 0141 } 0142 0143 connect(delegateTool(), SIGNAL(enclosingMaskProduced(KisPixelSelectionSP)), SLOT(slot_delegateTool_enclosingMaskProduced(KisPixelSelectionSP))); 0144 0145 if (isActivated()) { 0146 delegateTool()->activate(QSet<KoShape*>()); 0147 } 0148 } 0149 0150 bool KisToolEncloseAndFill::subtoolHasUserInteractionRunning() const 0151 { 0152 if (!delegateTool()) { 0153 return false; 0154 } 0155 0156 if (m_enclosingMethod == Rectangle) { 0157 return reinterpret_cast<KisRectangleEnclosingProducer*>(delegateTool())->hasUserInteractionRunning(); 0158 } else if (m_enclosingMethod == Ellipse) { 0159 return reinterpret_cast<KisEllipseEnclosingProducer*>(delegateTool())->hasUserInteractionRunning(); 0160 } else if (m_enclosingMethod == Path) { 0161 return reinterpret_cast<KisPathEnclosingProducer*>(delegateTool())->hasUserInteractionRunning(); 0162 } else if (m_enclosingMethod == Lasso) { 0163 return reinterpret_cast<KisLassoEnclosingProducer*>(delegateTool())->hasUserInteractionRunning(); 0164 } else if (m_enclosingMethod == Brush) { 0165 return reinterpret_cast<KisBrushEnclosingProducer*>(delegateTool())->hasUserInteractionRunning(); 0166 } 0167 return false; 0168 } 0169 0170 void KisToolEncloseAndFill::beginPrimaryAction(KoPointerEvent *event) 0171 { 0172 // cannot use enclose and fill tool on non-painting layers. 0173 // this logic triggers with multiple layer types like vector layer, clone layer, file layer, group layer 0174 if (currentNode().isNull() || currentNode()->inherits("KisShapeLayer") || nodePaintAbility() != NodePaintAbility::PAINT) { 0175 KisCanvas2 * kiscanvas = static_cast<KisCanvas2*>(canvas()); 0176 kiscanvas->viewManager()-> 0177 showFloatingMessage( 0178 i18n("You cannot use this tool with the selected layer type"), 0179 QIcon(), 2000, KisFloatingMessage::Medium, Qt::AlignCenter); 0180 event->ignore(); 0181 return; 0182 } 0183 0184 if (!nodeEditable()) { 0185 event->ignore(); 0186 return; 0187 } 0188 0189 KisDynamicDelegatedTool::beginPrimaryAction(event); 0190 } 0191 0192 void KisToolEncloseAndFill::activateAlternateAction(AlternateAction action) 0193 { 0194 if (subtoolHasUserInteractionRunning()) { 0195 if (delegateTool()) { 0196 delegateTool()->activatePrimaryAction(); 0197 } 0198 return; 0199 } 0200 KisDynamicDelegatedTool::activateAlternateAction(action); 0201 } 0202 0203 void KisToolEncloseAndFill::deactivateAlternateAction(AlternateAction action) 0204 { 0205 if (subtoolHasUserInteractionRunning()) { 0206 return; 0207 } 0208 KisDynamicDelegatedTool::deactivateAlternateAction(action); 0209 } 0210 0211 void KisToolEncloseAndFill::beginAlternateAction(KoPointerEvent *event, AlternateAction action) 0212 { 0213 if (subtoolHasUserInteractionRunning()) { 0214 if (delegateTool()) { 0215 delegateTool()->beginPrimaryAction(event); 0216 } 0217 return; 0218 } 0219 KisDynamicDelegatedTool::beginAlternateAction(event, action); 0220 m_alternateActionStarted = true; 0221 } 0222 0223 void KisToolEncloseAndFill::continueAlternateAction(KoPointerEvent *event, AlternateAction action) 0224 { 0225 if (subtoolHasUserInteractionRunning()) { 0226 if (delegateTool()) { 0227 delegateTool()->continuePrimaryAction(event); 0228 } 0229 return; 0230 } 0231 if (!m_alternateActionStarted) { 0232 return; 0233 } 0234 KisDynamicDelegatedTool::continueAlternateAction(event, action); 0235 } 0236 0237 void KisToolEncloseAndFill::endAlternateAction(KoPointerEvent *event, AlternateAction action) 0238 { 0239 if (subtoolHasUserInteractionRunning()) { 0240 if (delegateTool()) { 0241 delegateTool()->endPrimaryAction(event); 0242 } 0243 return; 0244 } 0245 if (!m_alternateActionStarted) { 0246 return; 0247 } 0248 KisDynamicDelegatedTool::endAlternateAction(event, action); 0249 m_alternateActionStarted = false; 0250 } 0251 0252 void KisToolEncloseAndFill::beginAlternateDoubleClickAction(KoPointerEvent *event, AlternateAction action) 0253 { 0254 if (subtoolHasUserInteractionRunning()) { 0255 if (delegateTool()) { 0256 delegateTool()->beginPrimaryDoubleClickAction(event); 0257 } 0258 return; 0259 } 0260 KisDynamicDelegatedTool::beginAlternateDoubleClickAction(event, action); 0261 } 0262 0263 void KisToolEncloseAndFill::slot_delegateTool_enclosingMaskProduced(KisPixelSelectionSP enclosingMask) 0264 { 0265 KisProcessingApplicator applicator(currentImage(), currentNode(), 0266 KisProcessingApplicator::SUPPORTS_WRAPAROUND_MODE, 0267 KisImageSignalVector(), 0268 kundo2_i18n("Enclose and Fill")); 0269 0270 KisResourcesSnapshotSP resources = 0271 new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); 0272 0273 if (m_reference == CurrentLayer) { 0274 m_referencePaintDevice = currentNode()->paintDevice(); 0275 } else if (m_reference == AllLayers) { 0276 m_referencePaintDevice = currentImage()->projection(); 0277 } else if (m_reference == ColorLabeledLayers) { 0278 if (!m_referenceNodeList) { 0279 m_referencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Enclose and Fill Tool Reference Result Paint Device"); 0280 m_referenceNodeList.reset(new KisMergeLabeledLayersCommand::ReferenceNodeInfoList); 0281 } 0282 KisPaintDeviceSP newReferencePaintDevice = KisMergeLabeledLayersCommand::createRefPaintDevice(image(), "Enclose and Fill Tool Reference Result Paint Device"); 0283 KisMergeLabeledLayersCommand::ReferenceNodeInfoListSP newReferenceNodeList(new KisMergeLabeledLayersCommand::ReferenceNodeInfoList); 0284 const int currentTime = image()->animationInterface()->currentTime(); 0285 applicator.applyCommand( 0286 new KisMergeLabeledLayersCommand( 0287 image(), 0288 m_referenceNodeList, 0289 newReferenceNodeList, 0290 m_referencePaintDevice, 0291 newReferencePaintDevice, 0292 m_selectedColorLabels, 0293 KisMergeLabeledLayersCommand::GroupSelectionPolicy_SelectIfColorLabeled, 0294 m_previousTime != currentTime 0295 ), 0296 KisStrokeJobData::SEQUENTIAL, 0297 KisStrokeJobData::EXCLUSIVE 0298 ); 0299 m_referencePaintDevice = newReferencePaintDevice; 0300 m_referenceNodeList = newReferenceNodeList; 0301 m_previousTime = currentTime; 0302 } 0303 0304 if (m_reference != ColorLabeledLayers) { 0305 // Reset this so that the device from color labeled layers gets 0306 // regenerated when that mode is selected again 0307 m_referenceNodeList.reset(); 0308 } 0309 0310 QTransform transform; 0311 transform.rotate(m_patternRotation); 0312 const qreal normalizedScale = m_patternScale * 0.01; 0313 transform.scale(normalizedScale, normalizedScale); 0314 resources->setFillTransform(transform); 0315 0316 KisProcessingVisitorSP visitor = 0317 new KisEncloseAndFillProcessingVisitor(m_referencePaintDevice, 0318 enclosingMask, 0319 resources->activeSelection(), 0320 resources, 0321 m_regionSelectionMethod, 0322 m_regionSelectionColor, 0323 m_regionSelectionInvert, 0324 m_regionSelectionIncludeContourRegions, 0325 false, 0326 m_fillThreshold, 0327 m_fillOpacitySpread, 0328 m_antiAlias, 0329 m_expand, 0330 m_stopGrowingAtDarkestPixel, 0331 m_feather, 0332 m_useSelectionAsBoundary, 0333 m_fillType == FillWithPattern, 0334 false, 0335 m_fillType == FillWithBackgroundColor, 0336 m_useCustomBlendingOptions, 0337 m_customOpacity * OPACITY_OPAQUE_U8 / 100, 0338 m_customCompositeOp); 0339 0340 applicator.applyVisitor(visitor, 0341 KisStrokeJobData::SEQUENTIAL, 0342 KisStrokeJobData::EXCLUSIVE); 0343 0344 applicator.end(); 0345 } 0346 0347 int KisToolEncloseAndFill::flags() const 0348 { 0349 return KisDynamicDelegatedTool::flags() | KisTool::FLAG_USES_CUSTOM_SIZE | KisTool::FLAG_USES_CUSTOM_PRESET; 0350 } 0351 0352 QWidget* KisToolEncloseAndFill::createOptionWidget() 0353 { 0354 loadConfiguration(); 0355 0356 // Create widgets 0357 KisOptionButtonStrip *optionButtonStripEnclosingMethod = 0358 new KisOptionButtonStrip; 0359 m_buttonEnclosingMethodRectangle = 0360 optionButtonStripEnclosingMethod->addButton( 0361 KisIconUtils::loadIcon("tool_rect_selection")); 0362 m_buttonEnclosingMethodEllipse = 0363 optionButtonStripEnclosingMethod->addButton( 0364 KisIconUtils::loadIcon("tool_elliptical_selection")); 0365 m_buttonEnclosingMethodPath = optionButtonStripEnclosingMethod->addButton( 0366 KisIconUtils::loadIcon("tool_path_selection")); 0367 m_buttonEnclosingMethodLasso = optionButtonStripEnclosingMethod->addButton( 0368 KisIconUtils::loadIcon("tool_outline_selection")); 0369 m_buttonEnclosingMethodBrush = optionButtonStripEnclosingMethod->addButton( 0370 KisIconUtils::loadIcon("krita_tool_freehand")); 0371 m_buttonEnclosingMethodLasso->setChecked(true); 0372 0373 m_comboBoxRegionSelectionMethod = new QComboBox; 0374 m_comboBoxRegionSelectionMethod->addItem( 0375 regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegions), 0376 static_cast<int>(RegionSelectionMethod::SelectAllRegions) 0377 ); 0378 m_comboBoxRegionSelectionMethod->addItem( 0379 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithSpecificColor), 0380 static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithSpecificColor) 0381 ); 0382 m_comboBoxRegionSelectionMethod->addItem( 0383 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithTransparent), 0384 static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithTransparent) 0385 ); 0386 m_comboBoxRegionSelectionMethod->addItem( 0387 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent), 0388 static_cast<int>(RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent) 0389 ); 0390 m_comboBoxRegionSelectionMethod->addItem( 0391 regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor), 0392 static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor) 0393 ); 0394 m_comboBoxRegionSelectionMethod->addItem( 0395 regionSelectionMethodToUserString(RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent), 0396 static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent) 0397 ); 0398 m_comboBoxRegionSelectionMethod->addItem( 0399 regionSelectionMethodToUserString( 0400 RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent 0401 ), 0402 static_cast<int>(RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent) 0403 ); 0404 m_comboBoxRegionSelectionMethod->addItem( 0405 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor), 0406 static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor) 0407 ); 0408 m_comboBoxRegionSelectionMethod->addItem( 0409 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedByTransparent), 0410 static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedByTransparent) 0411 ); 0412 m_comboBoxRegionSelectionMethod->addItem( 0413 regionSelectionMethodToUserString(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent), 0414 static_cast<int>(RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent) 0415 ); 0416 m_comboBoxRegionSelectionMethod->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); 0417 m_comboBoxRegionSelectionMethod->setMinimumContentsLength(15); 0418 m_comboBoxRegionSelectionMethod->view()->setMinimumWidth( 0419 m_comboBoxRegionSelectionMethod->view()->sizeHintForColumn(0) 0420 ); 0421 m_buttonRegionSelectionColor = new KisColorButton; 0422 m_checkBoxRegionSelectionInvert = 0423 new QCheckBox( 0424 i18nc("The 'invert' checkbox in enclose and fill tool", 0425 "Invert") 0426 ); 0427 m_checkBoxRegionSelectionIncludeContourRegions = 0428 new QCheckBox( 0429 i18nc("The 'include contour regions' checkbox in enclose and fill tool", 0430 "Include contour regions") 0431 ); 0432 m_checkBoxRegionSelectionIncludeContourRegions->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); 0433 0434 KisOptionButtonStrip *optionButtonStripFillWith = new KisOptionButtonStrip; 0435 m_buttonFillWithFG = optionButtonStripFillWith->addButton( 0436 KisIconUtils::loadIcon("object-order-lower-calligra")); 0437 m_buttonFillWithBG = optionButtonStripFillWith->addButton( 0438 KisIconUtils::loadIcon("object-order-raise-calligra")); 0439 m_buttonFillWithPattern = 0440 optionButtonStripFillWith->addButton(KisIconUtils::loadIcon("pattern")); 0441 m_buttonFillWithFG->setChecked(true); 0442 m_sliderPatternScale = new KisDoubleSliderSpinBox; 0443 m_sliderPatternScale->setRange(0, 10000, 2); 0444 m_sliderPatternScale->setSoftMaximum(500); 0445 KisSpinBoxI18nHelper::setText(m_sliderPatternScale, 0446 i18nc("The pattern 'scale' spinbox in enclose and fill tool options; {n} is the " 0447 "number value, % is the percent sign", 0448 "Scale: {n}%")); 0449 m_angleSelectorPatternRotation = new KisAngleSelector; 0450 m_angleSelectorPatternRotation->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_ContextMenu); 0451 m_angleSelectorPatternRotation->setIncreasingDirection(KisAngleGauge::IncreasingDirection_Clockwise); 0452 m_checkBoxCustomBlendingOptions = new QCheckBox(i18n("Use custom blending options")); 0453 m_sliderCustomOpacity = new KisSliderSpinBox; 0454 m_sliderCustomOpacity->setRange(0, 100); 0455 KisSpinBoxI18nHelper::setText(m_sliderCustomOpacity, 0456 i18nc("{n} is the number value, % is the percent sign", "Opacity: {n}%")); 0457 m_comboBoxCustomCompositeOp = new KisCompositeOpComboBox; 0458 0459 m_sliderFillThreshold = new KisSliderSpinBox; 0460 m_sliderFillThreshold->setPrefix(i18nc("The 'threshold' spinbox prefix in enclose and fill tool options", "Threshold: ")); 0461 m_sliderFillThreshold->setRange(1, 100); 0462 m_sliderFillOpacitySpread = new KisSliderSpinBox; 0463 KisSpinBoxI18nHelper::setText( 0464 m_sliderFillOpacitySpread, 0465 i18nc("The 'spread' spinbox in enclose and fill tool options; {n} is the number value, % is the percent sign", 0466 "Spread: {n}%")); 0467 m_sliderFillOpacitySpread->setRange(0, 100); 0468 m_checkBoxSelectionAsBoundary = 0469 new QCheckBox( 0470 i18nc("The 'use selection as boundary' checkbox in enclose and fill tool to use selection borders as boundary when filling", 0471 "Use selection as boundary") 0472 ); 0473 m_checkBoxSelectionAsBoundary->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); 0474 0475 m_checkBoxAntiAlias = new QCheckBox(i18nc("The anti-alias checkbox in enclose and fill tool options", "Anti-aliasing")); 0476 KisOptionCollectionWidget *containerGrow = new KisOptionCollectionWidget; 0477 m_sliderExpand = new KisSliderSpinBox; 0478 m_sliderExpand->setPrefix(i18nc("The 'grow/shrink' spinbox prefix in enclose and fill tool options", "Grow: ")); 0479 m_sliderExpand->setRange(-400, 400); 0480 m_sliderExpand->setSoftRange(-40, 40); 0481 m_sliderExpand->setSuffix(i18n(" px")); 0482 m_buttonStopGrowingAtDarkestPixel = new QToolButton; 0483 m_buttonStopGrowingAtDarkestPixel->setAutoRaise(true); 0484 m_buttonStopGrowingAtDarkestPixel->setCheckable(true); 0485 m_buttonStopGrowingAtDarkestPixel->setIcon(KisIconUtils::loadIcon("stop-at-boundary")); 0486 containerGrow->appendWidget("sliderExpand", m_sliderExpand); 0487 containerGrow->appendWidget("buttonStopGrowingAtDarkestPixel", m_buttonStopGrowingAtDarkestPixel); 0488 containerGrow->setOrientation(Qt::Horizontal); 0489 m_sliderFeather = new KisSliderSpinBox; 0490 m_sliderFeather->setPrefix(i18nc("The 'feather' spinbox prefix in enclose and fill tool options", "Feather: ")); 0491 m_sliderFeather->setRange(0, 400); 0492 m_sliderFeather->setSoftRange(0, 40); 0493 m_sliderFeather->setSuffix(i18n(" px")); 0494 0495 KisOptionButtonStrip *optionButtonStripReference = new KisOptionButtonStrip; 0496 m_buttonReferenceCurrent = optionButtonStripReference->addButton( 0497 KisIconUtils::loadIcon("current-layer")); 0498 m_buttonReferenceAll = optionButtonStripReference->addButton( 0499 KisIconUtils::loadIcon("all-layers")); 0500 m_buttonReferenceLabeled = 0501 optionButtonStripReference->addButton(KisIconUtils::loadIcon("tag")); 0502 m_buttonReferenceCurrent->setChecked(true); 0503 m_widgetLabels = new KisColorLabelSelectorWidget; 0504 m_widgetLabels->setExclusive(false); 0505 m_widgetLabels->setButtonSize(20); 0506 m_widgetLabels->setButtonWrapEnabled(true); 0507 m_widgetLabels->setMouseDragEnabled(true); 0508 0509 QPushButton *buttonReset = new QPushButton(i18nc("The 'reset' button in enclose and fill tool options", "Reset")); 0510 0511 // Set the tooltips 0512 m_buttonEnclosingMethodRectangle->setToolTip(i18n("Rectangle")); 0513 m_buttonEnclosingMethodEllipse->setToolTip(i18n("Ellipse")); 0514 m_buttonEnclosingMethodPath->setToolTip(i18n("Bezier Curve")); 0515 m_buttonEnclosingMethodLasso->setToolTip(i18n("Lasso")); 0516 m_buttonEnclosingMethodBrush->setToolTip(i18n("Brush")); 0517 m_comboBoxRegionSelectionMethod->setToolTip(regionSelectionMethodToUserString(m_regionSelectionMethod)); 0518 m_checkBoxRegionSelectionInvert->setToolTip(i18n("Enable to fill opposite regions instead")); 0519 m_checkBoxRegionSelectionIncludeContourRegions->setToolTip(i18n("Enable to also fill shapes that touch the contour of the enclosing region")); 0520 m_buttonFillWithFG->setToolTip(i18n("Foreground color")); 0521 m_buttonFillWithBG->setToolTip(i18n("Background color")); 0522 m_buttonFillWithPattern->setToolTip(i18n("Pattern")); 0523 m_sliderPatternScale->setToolTip(i18n("Set the scale of the pattern")); 0524 m_angleSelectorPatternRotation->setToolTip(i18n("Set the rotation of the pattern")); 0525 m_checkBoxCustomBlendingOptions->setToolTip(i18n("Set custom blending options instead of using the brush ones")); 0526 m_sliderCustomOpacity->setToolTip(i18n("Set a custom opacity for the fill")); 0527 m_comboBoxCustomCompositeOp->setToolTip(i18n("Set a custom blend mode for the fill")); 0528 0529 m_sliderFillThreshold->setToolTip(i18n("Set the color similarity tolerance of the fill. Increasing threshold increases the range of similar colors to be filled.")); 0530 m_sliderFillOpacitySpread->setToolTip(i18n("Set the extent of the opaque portion of the fill. Decreasing spread decreases opacity of fill areas depending on color similarity.")); 0531 m_checkBoxSelectionAsBoundary->setToolTip(i18n("Set if the contour of the active selection should be treated as a boundary when filling the region")); 0532 0533 m_checkBoxAntiAlias->setToolTip(i18n("Smooths the edges of the fill")); 0534 m_sliderExpand->setToolTip(i18n("Grow or shrink the fill by the set amount")); 0535 m_buttonStopGrowingAtDarkestPixel->setToolTip(i18n("Stop growing at the darkest and/or most opaque pixels")); 0536 m_sliderFeather->setToolTip(i18n("Blur the fill by the set amount")); 0537 0538 m_buttonReferenceCurrent->setToolTip(i18n("Fill regions found from the active layer")); 0539 m_buttonReferenceAll->setToolTip(i18n("Fill regions found from the merging of all layers")); 0540 m_buttonReferenceLabeled->setToolTip(i18n("Fill regions found from the merging of layers with specific color labels")); 0541 0542 buttonReset->setToolTip(i18n("Reset the options to their default values")); 0543 0544 // Construct the option widget 0545 m_optionWidget = new KisOptionCollectionWidget; 0546 m_optionWidget->setContentsMargins(0, 10, 0, 10); 0547 m_optionWidget->setSeparatorsVisible(true); 0548 0549 KisOptionCollectionWidgetWithHeader *sectionEnclosingMethod = 0550 new KisOptionCollectionWidgetWithHeader( 0551 i18nc("The 'enclosing method' section label in enclose and fill tool options", "Enclosing method") 0552 ); 0553 sectionEnclosingMethod->setPrimaryWidget(optionButtonStripEnclosingMethod); 0554 m_optionWidget->appendWidget("sectionEnclosingMethod", sectionEnclosingMethod); 0555 0556 KisOptionCollectionWidgetWithHeader *sectionReference = 0557 new KisOptionCollectionWidgetWithHeader( 0558 i18nc("The 'reference' section label in enclose and fill tool options", "Reference") 0559 ); 0560 sectionReference->setPrimaryWidget(optionButtonStripReference); 0561 sectionReference->appendWidget("widgetLabels", m_widgetLabels); 0562 sectionReference->setWidgetVisible("widgetLabels", false); 0563 m_optionWidget->appendWidget("sectionReference", sectionReference); 0564 0565 0566 KisOptionCollectionWidgetWithHeader *sectionWhatToFill = 0567 new KisOptionCollectionWidgetWithHeader( 0568 i18nc("The 'target regions' section label in enclose and fill tool options", "Target regions") 0569 ); 0570 sectionWhatToFill->setPrimaryWidget(m_comboBoxRegionSelectionMethod); 0571 sectionWhatToFill->appendWidget("buttonRegionSelectionColor", m_buttonRegionSelectionColor); 0572 sectionWhatToFill->appendWidget("checkBoxRegionSelectionInvert", m_checkBoxRegionSelectionInvert); 0573 sectionWhatToFill->appendWidget("checkBoxRegionSelectionIncludeContourRegions", m_checkBoxRegionSelectionIncludeContourRegions); 0574 m_optionWidget->appendWidget("sectionWhatToFill", sectionWhatToFill); 0575 0576 KisOptionCollectionWidgetWithHeader *sectionFillWith = 0577 new KisOptionCollectionWidgetWithHeader( 0578 i18nc("The 'fill source' section label in enclose and fill tool options", "Fill source") 0579 ); 0580 sectionFillWith->setPrimaryWidget(optionButtonStripFillWith); 0581 sectionFillWith->appendWidget("sliderPatternScale", m_sliderPatternScale); 0582 sectionFillWith->appendWidget("angleSelectorPatternRotation", m_angleSelectorPatternRotation); 0583 sectionFillWith->appendWidget("checkBoxCustomBlendingOptions", m_checkBoxCustomBlendingOptions); 0584 sectionFillWith->appendWidget("sliderCustomOpacity", m_sliderCustomOpacity); 0585 sectionFillWith->appendWidget("comboBoxCustomCompositeOp", m_comboBoxCustomCompositeOp); 0586 sectionFillWith->setWidgetVisible("sliderPatternScale", false); 0587 sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", false); 0588 m_optionWidget->appendWidget("sectionFillWith", sectionFillWith); 0589 0590 KisOptionCollectionWidgetWithHeader *sectionRegionExtent = 0591 new KisOptionCollectionWidgetWithHeader( 0592 i18nc("The 'fill extent' section label in enclose and fill tool options", "Fill extent") 0593 ); 0594 sectionRegionExtent->appendWidget("sliderThreshold", m_sliderFillThreshold); 0595 sectionRegionExtent->appendWidget("sliderSpread", m_sliderFillOpacitySpread); 0596 sectionRegionExtent->appendWidget("checkBoxSelectionAsBoundary", m_checkBoxSelectionAsBoundary); 0597 m_optionWidget->appendWidget("sectionRegionExtent", sectionRegionExtent); 0598 0599 KisOptionCollectionWidgetWithHeader *sectionAdjustments = 0600 new KisOptionCollectionWidgetWithHeader( 0601 i18nc("The 'adjustments' section label in enclose and fill tool options", "Adjustments") 0602 ); 0603 sectionAdjustments->appendWidget("checkBoxAntiAlias", m_checkBoxAntiAlias); 0604 sectionAdjustments->appendWidget("containerGrow", containerGrow); 0605 sectionAdjustments->appendWidget("sliderFeather", m_sliderFeather); 0606 m_optionWidget->appendWidget("sectionAdjustments", sectionAdjustments); 0607 0608 m_optionWidget->appendWidget("buttonReset", buttonReset); 0609 0610 // Initialize widgets 0611 if (m_enclosingMethod == Rectangle) { 0612 m_buttonEnclosingMethodRectangle->setChecked(true); 0613 } else if (m_enclosingMethod == Ellipse) { 0614 m_buttonEnclosingMethodEllipse->setChecked(true); 0615 } else if (m_enclosingMethod == Path) { 0616 m_buttonEnclosingMethodPath->setChecked(true); 0617 } else if (m_enclosingMethod == Lasso) { 0618 m_buttonEnclosingMethodLasso->setChecked(true); 0619 } else { 0620 m_buttonEnclosingMethodBrush->setChecked(true); 0621 } 0622 m_comboBoxRegionSelectionMethod->setCurrentIndex(m_comboBoxRegionSelectionMethod->findData(static_cast<int>(m_regionSelectionMethod))); 0623 m_buttonRegionSelectionColor->setColor(m_regionSelectionColor); 0624 sectionWhatToFill->setWidgetVisible( 0625 "buttonRegionSelectionColor", 0626 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor || 0627 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent || 0628 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor || 0629 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent || 0630 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor || 0631 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent 0632 ); 0633 m_checkBoxRegionSelectionInvert->setChecked(m_regionSelectionInvert); 0634 m_checkBoxRegionSelectionIncludeContourRegions->setChecked(m_regionSelectionIncludeContourRegions); 0635 sectionWhatToFill->setWidgetVisible( 0636 "checkBoxRegionSelectionIncludeContourRegions", 0637 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor || 0638 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent || 0639 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent || 0640 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor || 0641 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent || 0642 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent 0643 ); 0644 if (m_fillType == FillWithBackgroundColor) { 0645 m_buttonFillWithBG->setChecked(true); 0646 } else if (m_fillType == FillWithPattern) { 0647 m_buttonFillWithPattern->setChecked(true); 0648 sectionFillWith->setWidgetVisible("sliderPatternScale", true); 0649 sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", true); 0650 } 0651 m_sliderPatternScale->setValue(m_patternScale); 0652 m_angleSelectorPatternRotation->setAngle(m_patternRotation); 0653 m_checkBoxCustomBlendingOptions->setChecked(m_useCustomBlendingOptions); 0654 m_sliderCustomOpacity->setValue(m_customOpacity); 0655 slot_colorSpaceChanged(currentNode() && currentNode()->paintDevice() 0656 ? currentNode()->paintDevice()->colorSpace() 0657 : nullptr); 0658 m_comboBoxCustomCompositeOp->selectCompositeOp(KoID(m_customCompositeOp)); 0659 if (!m_useCustomBlendingOptions) { 0660 sectionFillWith->setWidgetVisible("sliderCustomOpacity", false); 0661 sectionFillWith->setWidgetVisible("comboBoxCustomCompositeOp", false); 0662 } 0663 m_sliderFillThreshold->setValue(m_fillThreshold); 0664 m_sliderFillOpacitySpread->setValue(m_fillOpacitySpread); 0665 m_checkBoxSelectionAsBoundary->setChecked(m_useSelectionAsBoundary); 0666 m_checkBoxAntiAlias->setChecked(m_antiAlias); 0667 m_sliderExpand->setValue(m_expand); 0668 m_buttonStopGrowingAtDarkestPixel->setChecked(m_stopGrowingAtDarkestPixel); 0669 m_sliderFeather->setValue(m_feather); 0670 if (m_reference == AllLayers) { 0671 m_buttonReferenceAll->setChecked(true); 0672 } else if (m_reference == ColorLabeledLayers) { 0673 m_buttonReferenceLabeled->setChecked(true); 0674 sectionReference->setWidgetVisible("widgetLabels", true); 0675 } 0676 m_widgetLabels->setSelection(m_selectedColorLabels); 0677 0678 // Make connections 0679 connect(optionButtonStripEnclosingMethod, 0680 SIGNAL(buttonToggled(KoGroupButton *, bool)), 0681 SLOT(slot_optionButtonStripEnclosingMethod_buttonToggled( 0682 KoGroupButton *, 0683 bool))); 0684 connect(m_comboBoxRegionSelectionMethod, 0685 SIGNAL(currentIndexChanged(int)), 0686 SLOT(slot_comboBoxRegionSelectionMethod_currentIndexChanged(int))); 0687 connect(m_buttonRegionSelectionColor, 0688 SIGNAL(changed(const KoColor&)), 0689 SLOT(slot_buttonRegionSelectionColor_changed(const KoColor&))); 0690 connect(m_checkBoxRegionSelectionInvert, 0691 SIGNAL(toggled(bool)), 0692 SLOT(slot_checkBoxRegionSelectionInvert_toggled(bool))); 0693 connect(m_checkBoxRegionSelectionIncludeContourRegions, 0694 SIGNAL(toggled(bool)), 0695 SLOT(slot_checkBoxRegionSelectionIncludeContourRegions_toggled(bool))); 0696 connect(optionButtonStripFillWith, 0697 SIGNAL(buttonToggled(KoGroupButton *, bool)), 0698 SLOT(slot_optionButtonStripFillWith_buttonToggled(KoGroupButton *, bool))); 0699 connect(m_sliderPatternScale, 0700 SIGNAL(valueChanged(double)), 0701 SLOT(slot_sliderPatternScale_valueChanged(double))); 0702 connect(m_angleSelectorPatternRotation, 0703 SIGNAL(angleChanged(double)), 0704 SLOT(slot_angleSelectorPatternRotation_angleChanged(double))); 0705 connect(m_checkBoxCustomBlendingOptions, 0706 SIGNAL(toggled(bool)), 0707 SLOT(slot_checkBoxUseCustomBlendingOptions_toggled(bool))); 0708 connect(m_sliderCustomOpacity, 0709 SIGNAL(valueChanged(int)), 0710 SLOT(slot_sliderCustomOpacity_valueChanged(int))); 0711 connect(m_comboBoxCustomCompositeOp, 0712 SIGNAL(currentIndexChanged(int)), 0713 SLOT(slot_comboBoxCustomCompositeOp_currentIndexChanged(int))); 0714 connect(m_sliderFillThreshold, 0715 SIGNAL(valueChanged(int)), 0716 SLOT(slot_sliderFillThreshold_valueChanged(int))); 0717 connect(m_sliderFillOpacitySpread, 0718 SIGNAL(valueChanged(int)), 0719 SLOT(slot_sliderFillOpacitySpread_valueChanged(int))); 0720 connect(m_checkBoxSelectionAsBoundary, 0721 SIGNAL(toggled(bool)), 0722 SLOT(slot_checkBoxSelectionAsBoundary_toggled(bool))); 0723 connect(m_checkBoxAntiAlias, 0724 SIGNAL(toggled(bool)), 0725 SLOT(slot_checkBoxAntiAlias_toggled(bool))); 0726 connect(m_sliderExpand, 0727 SIGNAL(valueChanged(int)), 0728 SLOT(slot_sliderExpand_valueChanged(int))); 0729 connect(m_buttonStopGrowingAtDarkestPixel, 0730 SIGNAL(toggled(bool)), 0731 SLOT(slot_buttonStopGrowingAtDarkestPixel_toggled(bool))); 0732 connect(m_sliderFeather, 0733 SIGNAL(valueChanged(int)), 0734 SLOT(slot_sliderFeather_valueChanged(int))); 0735 connect(optionButtonStripReference, 0736 SIGNAL(buttonToggled(KoGroupButton *, bool)), 0737 SLOT(slot_optionButtonStripReference_buttonToggled(KoGroupButton *, bool))); 0738 connect(m_widgetLabels, 0739 SIGNAL(selectionChanged()), 0740 SLOT(slot_widgetLabels_selectionChanged())); 0741 connect(buttonReset, 0742 SIGNAL(clicked()), 0743 SLOT(slot_buttonReset_clicked())); 0744 0745 return m_optionWidget; 0746 } 0747 0748 void KisToolEncloseAndFill::loadConfiguration() 0749 { 0750 m_enclosingMethod = loadEnclosingMethodFromConfig(); 0751 m_regionSelectionMethod = loadRegionSelectionMethodFromConfig(); 0752 m_regionSelectionColor = loadRegionSelectionColorFromConfig(); 0753 m_regionSelectionInvert = m_configGroup.readEntry<bool>("regionSelectionInvert", false); 0754 m_regionSelectionIncludeContourRegions = m_configGroup.readEntry<bool>("regionSelectionIncludeContourRegions", false); 0755 { 0756 const QString fillTypeStr = m_configGroup.readEntry<QString>("fillWith", ""); 0757 if (fillTypeStr == "foregroundColor") { 0758 m_fillType = FillWithForegroundColor; 0759 } else if (fillTypeStr == "backgroundColor") { 0760 m_fillType = FillWithBackgroundColor; 0761 } else if (fillTypeStr == "pattern") { 0762 m_fillType = FillWithPattern; 0763 } else { 0764 if (m_configGroup.readEntry<bool>("usePattern", false)) { 0765 m_fillType = FillWithPattern; 0766 } else { 0767 m_fillType = FillWithForegroundColor; 0768 } 0769 } 0770 } 0771 m_patternScale = m_configGroup.readEntry<qreal>("patternScale", 100.0); 0772 m_patternRotation = m_configGroup.readEntry<qreal>("patternRotate", 0.0); 0773 m_useCustomBlendingOptions = m_configGroup.readEntry<bool>("useCustomBlendingOptions", false); 0774 m_customOpacity = qBound(0, m_configGroup.readEntry<int>("customOpacity", 100), 100); 0775 m_customCompositeOp = m_configGroup.readEntry<QString>("customCompositeOp", COMPOSITE_OVER); 0776 if (KoCompositeOpRegistry::instance().getKoID(m_customCompositeOp).id().isNull()) { 0777 m_customCompositeOp = COMPOSITE_OVER; 0778 } 0779 m_fillThreshold = m_configGroup.readEntry<int>("fillThreshold", 8); 0780 m_fillOpacitySpread = m_configGroup.readEntry<int>("fillOpacitySpread", 100); 0781 m_useSelectionAsBoundary = m_configGroup.readEntry<bool>("useSelectionAsBoundary", true); 0782 m_antiAlias = m_configGroup.readEntry<bool>("antiAlias", false); 0783 m_expand = m_configGroup.readEntry<int>("expand", 0); 0784 m_stopGrowingAtDarkestPixel = m_configGroup.readEntry<bool>("stopGrowingAtDarkestPixel", false); 0785 m_feather = m_configGroup.readEntry<int>("feather", 0); 0786 { 0787 const QString sampleLayersModeStr = m_configGroup.readEntry<QString>("reference", "currentLayer"); 0788 if (sampleLayersModeStr == "allLayers") { 0789 m_reference = AllLayers; 0790 } else if (sampleLayersModeStr == "colorLabeledLayers") { 0791 m_reference = ColorLabeledLayers; 0792 } else { 0793 m_reference = CurrentLayer; 0794 } 0795 } 0796 { 0797 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0798 const QStringList colorLabelsStr = m_configGroup.readEntry<QString>("colorLabels", "").split(',', Qt::SkipEmptyParts); 0799 #else 0800 const QStringList colorLabelsStr = m_configGroup.readEntry<QString>("colorLabels", "").split(',', QString::SkipEmptyParts); 0801 #endif 0802 0803 m_selectedColorLabels.clear(); 0804 for (const QString &colorLabelStr : colorLabelsStr) { 0805 bool ok; 0806 const int colorLabel = colorLabelStr.toInt(&ok); 0807 if (ok) { 0808 m_selectedColorLabels << colorLabel; 0809 } 0810 } 0811 } 0812 0813 setupEnclosingSubtool(); 0814 } 0815 0816 KisToolEncloseAndFill::EnclosingMethod KisToolEncloseAndFill::loadEnclosingMethodFromConfig() const 0817 { 0818 return configStringToEnclosingMethod(m_configGroup.readEntry("enclosingMethod", enclosingMethodToConfigString(defaultEnclosingMethod()))); 0819 } 0820 0821 void KisToolEncloseAndFill::saveEnclosingMethodToConfig(EnclosingMethod enclosingMethod) 0822 { 0823 m_configGroup.writeEntry("enclosingMethod", enclosingMethodToConfigString(enclosingMethod)); 0824 } 0825 0826 QString KisToolEncloseAndFill::enclosingMethodToConfigString(EnclosingMethod enclosingMethod) const 0827 { 0828 switch (enclosingMethod) { 0829 case Rectangle: return "rectangle"; 0830 case Ellipse: return "ellipse"; 0831 case Path: return "path"; 0832 case Brush: return "brush"; 0833 default: return "lasso"; 0834 } 0835 } 0836 0837 KisToolEncloseAndFill::EnclosingMethod KisToolEncloseAndFill::configStringToEnclosingMethod(const QString &configString) const 0838 { 0839 if (configString == "rectangle") { 0840 return Rectangle; 0841 } else if (configString == "ellipse") { 0842 return Ellipse; 0843 } else if (configString == "path") { 0844 return Path; 0845 } else if (configString == "brush") { 0846 return Brush; 0847 } 0848 return Lasso; 0849 } 0850 0851 QString KisToolEncloseAndFill::regionSelectionMethodToUserString(RegionSelectionMethod regionSelectionMethod) const 0852 { 0853 if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegions) { 0854 return i18nc("Region selection method in enclose and fill tool", 0855 "All"); 0856 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor) { 0857 return i18nc("Region selection method in enclose and fill tool", 0858 "Specific color"); 0859 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent) { 0860 return i18nc("Region selection method in enclose and fill tool", 0861 "Transparency"); 0862 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent) { 0863 return i18nc("Region selection method in enclose and fill tool", 0864 "Specific color or transparency"); 0865 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor) { 0866 return i18nc("Region selection method in enclose and fill tool", 0867 "All, excluding a specific color"); 0868 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent) { 0869 return i18nc("Region selection method in enclose and fill tool", 0870 "All, excluding transparency"); 0871 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent) { 0872 return i18nc("Region selection method in enclose and fill tool", 0873 "All, excluding a specific color or transparency"); 0874 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor) { 0875 return i18nc("Region selection method in enclose and fill tool", 0876 "Any surrounded by a specific color"); 0877 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedByTransparent) { 0878 return i18nc("Region selection method in enclose and fill tool", 0879 "Any surrounded by transparency"); 0880 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent) { 0881 return i18nc("Region selection method in enclose and fill tool", 0882 "Any surrounded by a specific color or transparency"); 0883 } 0884 return QString(); 0885 } 0886 0887 KisToolEncloseAndFill::RegionSelectionMethod KisToolEncloseAndFill::loadRegionSelectionMethodFromConfig() const 0888 { 0889 return configStringToRegionSelectionMethod(m_configGroup.readEntry("regionSelectionMethod", regionSelectionMethodToConfigString(defaultRegionSelectionMethod()))); 0890 } 0891 0892 void KisToolEncloseAndFill::saveRegionSelectionMethodToConfig(RegionSelectionMethod regionSelectionMethod) 0893 { 0894 m_configGroup.writeEntry("regionSelectionMethod", regionSelectionMethodToConfigString(regionSelectionMethod)); 0895 } 0896 0897 QString KisToolEncloseAndFill::regionSelectionMethodToConfigString(RegionSelectionMethod regionSelectionMethod) const 0898 { 0899 if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegions) { 0900 return "allRegions"; 0901 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor) { 0902 return "regionsFilledWithSpecificColor"; 0903 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent) { 0904 return "regionsFilledWithTransparent"; 0905 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent) { 0906 return "regionsFilledWithSpecificColorOrTransparent"; 0907 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor) { 0908 return "allRegionsExceptFilledWithSpecificColor"; 0909 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent) { 0910 return "allRegionsExceptFilledWithTransparent"; 0911 } else if (regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent) { 0912 return "allRegionsExceptFilledWithSpecificColorOrTransparent"; 0913 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor) { 0914 return "regionsSurroundedBySpecificColor"; 0915 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedByTransparent) { 0916 return "regionsSurroundedByTransparent"; 0917 } else if (regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent) { 0918 return "regionsSurroundedBySpecificColorOrTransparent"; 0919 } 0920 return QString(); 0921 } 0922 0923 KisToolEncloseAndFill::RegionSelectionMethod KisToolEncloseAndFill::configStringToRegionSelectionMethod(const QString &configString) const 0924 { 0925 if (configString == "regionsFilledWithSpecificColor") { 0926 return RegionSelectionMethod::SelectRegionsFilledWithSpecificColor; 0927 } else if (configString == "regionsFilledWithTransparent") { 0928 return RegionSelectionMethod::SelectRegionsFilledWithTransparent; 0929 } else if (configString == "regionsFilledWithSpecificColorOrTransparent") { 0930 return RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent; 0931 } else if (configString == "allRegionsExceptFilledWithSpecificColor") { 0932 return RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor; 0933 } else if (configString == "allRegionsExceptFilledWithTransparent") { 0934 return RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent; 0935 } else if (configString == "allRegionsExceptFilledWithSpecificColorOrTransparent") { 0936 return RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent; 0937 } else if (configString == "regionsSurroundedBySpecificColor") { 0938 return RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor; 0939 } else if (configString == "regionsSurroundedByTransparent") { 0940 return RegionSelectionMethod::SelectRegionsSurroundedByTransparent; 0941 } else if (configString == "regionsSurroundedBySpecificColorOrTransparent") { 0942 return RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent; 0943 } 0944 return RegionSelectionMethod::SelectAllRegions; 0945 } 0946 0947 KoColor KisToolEncloseAndFill::loadRegionSelectionColorFromConfig() 0948 { 0949 const QString xmlColor = m_configGroup.readEntry("regionSelectionColor", QString()); 0950 QDomDocument doc; 0951 if (doc.setContent(xmlColor)) { 0952 QDomElement e = doc.documentElement().firstChild().toElement(); 0953 QString channelDepthID = doc.documentElement().attribute("channeldepth", Integer16BitsColorDepthID.id()); 0954 bool ok; 0955 if (e.hasAttribute("space") || e.tagName().toLower() == "srgb") { 0956 return KoColor::fromXML(e, channelDepthID, &ok); 0957 } else if (doc.documentElement().hasAttribute("space") || doc.documentElement().tagName().toLower() == "srgb"){ 0958 return KoColor::fromXML(doc.documentElement(), channelDepthID, &ok); 0959 } 0960 } 0961 return KoColor(); 0962 } 0963 0964 KisToolEncloseAndFill::Reference KisToolEncloseAndFill::loadReferenceFromConfig() const 0965 { 0966 if (m_configGroup.hasKey("reference")) { 0967 return configStringToReference(m_configGroup.readEntry("reference", referenceToConfigString(defaultReference()))); 0968 } else { 0969 bool sampleMerged = m_configGroup.readEntry("sampleMerged", false); 0970 return sampleMerged ? AllLayers : CurrentLayer; 0971 } 0972 return CurrentLayer; 0973 } 0974 0975 void KisToolEncloseAndFill::saveReferenceToConfig(Reference reference) 0976 { 0977 m_configGroup.writeEntry("reference", referenceToConfigString(reference)); 0978 } 0979 0980 QString KisToolEncloseAndFill::referenceToConfigString(Reference reference) const 0981 { 0982 if (reference == AllLayers) { 0983 return "allLayers"; 0984 } else if (reference == ColorLabeledLayers) { 0985 return "colorLabeledLayers"; 0986 } 0987 return "currentLayer"; 0988 } 0989 0990 KisToolEncloseAndFill::Reference KisToolEncloseAndFill::configStringToReference(const QString &configString) const 0991 { 0992 if (configString == "allLayers") { 0993 return AllLayers; 0994 } else if (configString == "colorLabeledLayers") { 0995 return ColorLabeledLayers; 0996 } 0997 return CurrentLayer; 0998 } 0999 1000 void KisToolEncloseAndFill::slot_optionButtonStripEnclosingMethod_buttonToggled( 1001 KoGroupButton *button, 1002 bool checked) 1003 { 1004 if (!checked) { 1005 return; 1006 } 1007 1008 if (button == m_buttonEnclosingMethodRectangle) { 1009 m_enclosingMethod = Rectangle; 1010 } else if (button == m_buttonEnclosingMethodEllipse) { 1011 m_enclosingMethod = Ellipse; 1012 } else if (button == m_buttonEnclosingMethodPath) { 1013 m_enclosingMethod = Path; 1014 } else if (button == m_buttonEnclosingMethodLasso) { 1015 m_enclosingMethod = Lasso; 1016 } else { 1017 m_enclosingMethod = Brush; 1018 } 1019 1020 saveEnclosingMethodToConfig(m_enclosingMethod); 1021 setupEnclosingSubtool(); 1022 } 1023 1024 void KisToolEncloseAndFill::slot_comboBoxRegionSelectionMethod_currentIndexChanged(int) 1025 { 1026 m_regionSelectionMethod = static_cast<RegionSelectionMethod>(m_comboBoxRegionSelectionMethod->currentData().toInt()); 1027 1028 KisOptionCollectionWidgetWithHeader *sectionWhatToFill = 1029 m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionWhatToFill"); 1030 sectionWhatToFill->setWidgetVisible("buttonRegionSelectionColor", 1031 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor || 1032 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent || 1033 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor || 1034 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent || 1035 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColor || 1036 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsSurroundedBySpecificColorOrTransparent 1037 ); 1038 sectionWhatToFill->setWidgetVisible( 1039 "checkBoxRegionSelectionIncludeContourRegions", 1040 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColor || 1041 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithTransparent || 1042 m_regionSelectionMethod == RegionSelectionMethod::SelectRegionsFilledWithSpecificColorOrTransparent || 1043 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColor || 1044 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithTransparent || 1045 m_regionSelectionMethod == RegionSelectionMethod::SelectAllRegionsExceptFilledWithSpecificColorOrTransparent 1046 ); 1047 1048 m_comboBoxRegionSelectionMethod->setToolTip(m_comboBoxRegionSelectionMethod->currentText()); 1049 1050 saveRegionSelectionMethodToConfig(m_regionSelectionMethod); 1051 } 1052 1053 void KisToolEncloseAndFill::slot_buttonRegionSelectionColor_changed(const KoColor &color) 1054 { 1055 if (color == m_regionSelectionColor) { 1056 return; 1057 } 1058 m_regionSelectionColor = color; 1059 m_configGroup.writeEntry("regionSelectionColor", color.toXML()); 1060 } 1061 1062 void KisToolEncloseAndFill::slot_checkBoxRegionSelectionInvert_toggled(bool checked) 1063 { 1064 if (checked == m_regionSelectionInvert) { 1065 return; 1066 } 1067 m_regionSelectionInvert = checked; 1068 m_configGroup.writeEntry("regionSelectionInvert", checked); 1069 } 1070 1071 void KisToolEncloseAndFill::slot_checkBoxRegionSelectionIncludeContourRegions_toggled(bool checked) 1072 { 1073 if (checked == m_regionSelectionIncludeContourRegions) { 1074 return; 1075 } 1076 m_regionSelectionIncludeContourRegions = checked; 1077 m_configGroup.writeEntry("regionSelectionIncludeContourRegions", checked); 1078 } 1079 1080 void KisToolEncloseAndFill::slot_optionButtonStripFillWith_buttonToggled( 1081 KoGroupButton *button, 1082 bool checked) 1083 { 1084 if (!checked) { 1085 return; 1086 } 1087 const bool visible = button == m_buttonFillWithPattern; 1088 KisOptionCollectionWidgetWithHeader *sectionFillWith = 1089 m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionFillWith"); 1090 sectionFillWith->setWidgetVisible("sliderPatternScale", visible); 1091 sectionFillWith->setWidgetVisible("angleSelectorPatternRotation", visible); 1092 1093 m_fillType = button == m_buttonFillWithFG ? FillWithForegroundColor 1094 : (button == m_buttonFillWithBG ? FillWithBackgroundColor : FillWithPattern); 1095 1096 m_configGroup.writeEntry( 1097 "fillWith", 1098 button == m_buttonFillWithFG ? "foregroundColor" : (button == m_buttonFillWithBG ? "backgroundColor" : "pattern") 1099 ); 1100 } 1101 1102 void KisToolEncloseAndFill::slot_sliderPatternScale_valueChanged(double value) 1103 { 1104 if (value == m_patternScale) { 1105 return; 1106 } 1107 m_patternScale = value; 1108 m_configGroup.writeEntry("patternScale", value); 1109 } 1110 1111 void KisToolEncloseAndFill::slot_angleSelectorPatternRotation_angleChanged(double value) 1112 { 1113 if (value == m_patternRotation) { 1114 return; 1115 } 1116 m_patternRotation = value; 1117 m_configGroup.writeEntry("patternRotate", value); 1118 } 1119 1120 void KisToolEncloseAndFill::slot_checkBoxUseCustomBlendingOptions_toggled(bool checked) 1121 { 1122 KisOptionCollectionWidgetWithHeader *sectionFillWith = 1123 m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionFillWith"); 1124 sectionFillWith->setWidgetVisible("sliderCustomOpacity", checked); 1125 sectionFillWith->setWidgetVisible("comboBoxCustomCompositeOp", checked); 1126 m_useCustomBlendingOptions = checked; 1127 m_configGroup.writeEntry("useCustomBlendingOptions", checked); 1128 } 1129 1130 void KisToolEncloseAndFill::slot_sliderCustomOpacity_valueChanged(int value) 1131 { 1132 if (value == m_customOpacity) { 1133 return; 1134 } 1135 m_customOpacity = value; 1136 m_configGroup.writeEntry("customOpacity", value); 1137 } 1138 1139 void KisToolEncloseAndFill::slot_comboBoxCustomCompositeOp_currentIndexChanged(int index) 1140 { 1141 Q_UNUSED(index); 1142 const QString compositeOpId = m_comboBoxCustomCompositeOp->selectedCompositeOp().id(); 1143 if (compositeOpId == m_customCompositeOp) { 1144 return; 1145 } 1146 m_customCompositeOp = compositeOpId; 1147 m_configGroup.writeEntry("customCompositeOp", compositeOpId); 1148 } 1149 1150 void KisToolEncloseAndFill::slot_sliderFillThreshold_valueChanged(int value) 1151 { 1152 if (value == m_fillThreshold) { 1153 return; 1154 } 1155 m_fillThreshold = value; 1156 m_configGroup.writeEntry("fillThreshold", value); 1157 } 1158 1159 void KisToolEncloseAndFill::slot_sliderFillOpacitySpread_valueChanged(int value) 1160 { 1161 if (value == m_fillOpacitySpread) { 1162 return; 1163 } 1164 m_fillOpacitySpread = value; 1165 m_configGroup.writeEntry("fillOpacitySpread", value); 1166 } 1167 1168 void KisToolEncloseAndFill::slot_checkBoxSelectionAsBoundary_toggled(bool checked) 1169 { 1170 if (checked == m_useSelectionAsBoundary) { 1171 return; 1172 } 1173 m_useSelectionAsBoundary = checked; 1174 m_configGroup.writeEntry("useSelectionAsBoundary", checked); 1175 } 1176 1177 void KisToolEncloseAndFill::slot_checkBoxAntiAlias_toggled(bool checked) 1178 { 1179 if (checked == m_antiAlias) { 1180 return; 1181 } 1182 m_antiAlias = checked; 1183 m_configGroup.writeEntry("antiAlias", checked); 1184 } 1185 1186 void KisToolEncloseAndFill::slot_sliderExpand_valueChanged(int value) 1187 { 1188 if (value == m_expand) { 1189 return; 1190 } 1191 m_expand = value; 1192 m_configGroup.writeEntry("expand", value); 1193 } 1194 1195 void KisToolEncloseAndFill::slot_buttonStopGrowingAtDarkestPixel_toggled(bool enabled) 1196 { 1197 if (enabled == m_stopGrowingAtDarkestPixel) { 1198 return; 1199 } 1200 m_stopGrowingAtDarkestPixel = enabled; 1201 m_configGroup.writeEntry("stopGrowingAtDarkestPixel", enabled); 1202 } 1203 1204 void KisToolEncloseAndFill::slot_sliderFeather_valueChanged(int value) 1205 { 1206 if (value == m_feather) { 1207 return; 1208 } 1209 m_feather = value; 1210 m_configGroup.writeEntry("feather", value); 1211 } 1212 1213 void KisToolEncloseAndFill::slot_optionButtonStripReference_buttonToggled( 1214 KoGroupButton *button, 1215 bool checked) 1216 { 1217 if (!checked) { 1218 return; 1219 } 1220 KisOptionCollectionWidgetWithHeader *sectionReference = 1221 m_optionWidget->widgetAs<KisOptionCollectionWidgetWithHeader*>("sectionReference"); 1222 sectionReference->setWidgetVisible("widgetLabels", button == m_buttonReferenceLabeled); 1223 1224 m_reference = button == m_buttonReferenceCurrent ? CurrentLayer 1225 : (button == m_buttonReferenceAll ? AllLayers : ColorLabeledLayers); 1226 1227 m_configGroup.writeEntry( 1228 "reference", 1229 button == m_buttonReferenceCurrent ? "currentLayer" : (button == m_buttonReferenceAll ? "allLayers" : "colorLabeledLayers") 1230 ); 1231 } 1232 1233 void KisToolEncloseAndFill::slot_widgetLabels_selectionChanged() 1234 { 1235 QList<int> labels = m_widgetLabels->selection(); 1236 if (labels == m_selectedColorLabels) { 1237 return; 1238 } 1239 m_selectedColorLabels = labels; 1240 if (labels.isEmpty()) { 1241 return; 1242 } 1243 QString colorLabels = QString::number(labels.first()); 1244 for (int i = 1; i < labels.size(); ++i) { 1245 colorLabels += "," + QString::number(labels[i]); 1246 } 1247 m_configGroup.writeEntry("colorLabels", colorLabels); 1248 } 1249 1250 void KisToolEncloseAndFill::slot_buttonReset_clicked() 1251 { 1252 m_buttonEnclosingMethodLasso->setChecked(true); 1253 m_comboBoxRegionSelectionMethod->setCurrentIndex( 1254 m_comboBoxRegionSelectionMethod->findData(static_cast<int>(RegionSelectionMethod::SelectAllRegions)) 1255 ); 1256 m_buttonRegionSelectionColor->setColor(KoColor()); 1257 m_checkBoxRegionSelectionInvert->setChecked(false); 1258 m_checkBoxRegionSelectionIncludeContourRegions->setChecked(false); 1259 m_buttonFillWithFG->setChecked(true); 1260 m_sliderPatternScale->setValue(100.0); 1261 m_angleSelectorPatternRotation->setAngle(0.0); 1262 m_checkBoxCustomBlendingOptions->setChecked(false); 1263 m_sliderCustomOpacity->setValue(100); 1264 m_comboBoxCustomCompositeOp->selectCompositeOp(KoID(COMPOSITE_OVER)); 1265 m_sliderFillThreshold->setValue(8); 1266 m_sliderFillOpacitySpread->setValue(100); 1267 m_checkBoxSelectionAsBoundary->setChecked(true); 1268 m_checkBoxAntiAlias->setChecked(false); 1269 m_sliderExpand->setValue(0); 1270 m_buttonStopGrowingAtDarkestPixel->setChecked(false); 1271 m_sliderFeather->setValue(0); 1272 m_buttonReferenceCurrent->setChecked(true); 1273 m_widgetLabels->setSelection({}); 1274 } 1275 1276 void KisToolEncloseAndFill::slot_currentNodeChanged(const KisNodeSP node) 1277 { 1278 if (m_previousNode && m_previousNode->paintDevice()) { 1279 disconnect(m_previousNode->paintDevice().data(), 1280 SIGNAL(colorSpaceChanged(const KoColorSpace*)), 1281 this, 1282 SLOT(slot_colorSpaceChanged(const KoColorSpace*))); 1283 } 1284 if (node && node->paintDevice()) { 1285 connect(node->paintDevice().data(), 1286 SIGNAL(colorSpaceChanged(const KoColorSpace*)), 1287 this, 1288 SLOT(slot_colorSpaceChanged(const KoColorSpace*))); 1289 slot_colorSpaceChanged(node->paintDevice()->colorSpace()); 1290 } 1291 m_previousNode = node; 1292 } 1293 1294 void KisToolEncloseAndFill::slot_colorSpaceChanged(const KoColorSpace *colorSpace) 1295 { 1296 if (!m_comboBoxCustomCompositeOp) { 1297 return; 1298 } 1299 const KoColorSpace *compositionSpace = colorSpace; 1300 if (currentNode() && currentNode()->paintDevice()) { 1301 // Currently, composition source is enough to determine the available blending mode, 1302 // because either destination is the same (paint layers), or composition happens 1303 // in source space (masks). 1304 compositionSpace = currentNode()->paintDevice()->compositionSourceColorSpace(); 1305 } 1306 m_comboBoxCustomCompositeOp->validate(compositionSpace); 1307 }