Warning, file /graphics/krita/plugins/tools/tool_lazybrush/kis_tool_lazy_brush_options_widget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kis_tool_lazy_brush_options_widget.h" 0008 0009 #include "ui_kis_tool_lazy_brush_options_widget.h" 0010 0011 #include <QWheelEvent> 0012 #include <KoColorSpaceRegistry.h> 0013 #include "KisPaletteModel.h" 0014 #include <KisSpinBoxI18nHelper.h> 0015 0016 #include "kis_config.h" 0017 #include <resources/KoColorSet.h> 0018 #include "kis_canvas_resource_provider.h" 0019 #include "kis_signal_auto_connection.h" 0020 #include "lazybrush/kis_colorize_mask.h" 0021 #include "kis_image.h" 0022 #include "kis_signals_blocker.h" 0023 #include "kis_signal_compressor.h" 0024 #include "kis_layer_properties_icons.h" 0025 0026 struct KisToolLazyBrushOptionsWidget::Private 0027 { 0028 Private() 0029 : baseNodeChangedCompressor(500, KisSignalCompressor::FIRST_ACTIVE) 0030 { 0031 } 0032 0033 Ui_KisToolLazyBrushOptionsWidget *ui {nullptr}; 0034 KisPaletteModel *colorModel {nullptr}; 0035 int preferredColumnCount {10}; 0036 KisCanvasResourceProvider *provider {nullptr}; 0037 0038 KisSignalAutoConnectionsStore providerSignals; 0039 KisSignalAutoConnectionsStore maskSignals; 0040 KisColorizeMaskSP activeMask; 0041 0042 KoColorSetSP colorSet {new KoColorSet(QString())}; 0043 int transparentColorIndex {0}; 0044 0045 KisSignalCompressor baseNodeChangedCompressor; 0046 }; 0047 0048 struct PaletteEventFilter : public QObject 0049 { 0050 bool eventFilter(QObject *watched, QEvent *event) override 0051 { 0052 if (event->type() == QEvent::Wheel) { 0053 QWheelEvent *wevent = static_cast<QWheelEvent*>(event); 0054 0055 if (wevent->modifiers() == Qt::ControlModifier) { 0056 if (watched == m_parentView->viewport()) { 0057 const int columnCountDelta = -wevent->delta() / QWheelEvent::DefaultDeltasPerStep; 0058 const int newColumnCount = qMax(1, m_optionsWidget->m_d->preferredColumnCount + columnCountDelta); 0059 0060 m_optionsWidget->m_d->preferredColumnCount = newColumnCount; 0061 m_optionsWidget->slotColorLabelsChanged(); 0062 } 0063 0064 return true; 0065 } 0066 0067 } 0068 0069 return QObject::eventFilter(watched, event); 0070 } 0071 0072 PaletteEventFilter(KisPaletteView *parentView, KisToolLazyBrushOptionsWidget *optionsWidget) 0073 : QObject(optionsWidget), 0074 m_parentView(parentView), 0075 m_optionsWidget(optionsWidget) 0076 0077 {} 0078 0079 KisPaletteView *m_parentView; 0080 KisToolLazyBrushOptionsWidget *m_optionsWidget; 0081 }; 0082 0083 KisToolLazyBrushOptionsWidget::KisToolLazyBrushOptionsWidget(KisCanvasResourceProvider *provider, QWidget *parent) 0084 : QWidget(parent), 0085 m_d(new Private) 0086 { 0087 m_d->ui = new Ui_KisToolLazyBrushOptionsWidget(); 0088 m_d->ui->setupUi(this); 0089 0090 m_d->colorModel = new KisPaletteModel(this); 0091 m_d->ui->colorView->setPaletteModel(m_d->colorModel); 0092 m_d->ui->colorView->setAllowModification(false); //people probably shouldn't be able to edit the colorentries themselves. 0093 m_d->ui->colorView->setCrossedKeyword("transparent"); 0094 0095 PaletteEventFilter *filter = new PaletteEventFilter(m_d->ui->colorView, this); 0096 m_d->ui->colorView->viewport()->installEventFilter(filter); 0097 0098 connect(m_d->ui->chkUseEdgeDetection, SIGNAL(toggled(bool)), SLOT(slotUseEdgeDetectionChanged(bool))); 0099 connect(m_d->ui->intEdgeDetectionSize, SIGNAL(valueChanged(int)), SLOT(slotEdgeDetectionSizeChanged(int))); 0100 connect(m_d->ui->intRadius, SIGNAL(valueChanged(int)), SLOT(slotRadiusChanged(int))); 0101 connect(m_d->ui->intCleanUp, SIGNAL(valueChanged(int)), SLOT(slotCleanUpChanged(int))); 0102 connect(m_d->ui->chkLimitToDevice, SIGNAL(toggled(bool)), SLOT(slotLimitToDeviceChanged(bool))); 0103 0104 m_d->ui->intEdgeDetectionSize->setRange(0, 100); 0105 m_d->ui->intEdgeDetectionSize->setExponentRatio(2.0); 0106 m_d->ui->intEdgeDetectionSize->setSuffix(i18n(" px")); 0107 m_d->ui->intEdgeDetectionSize->setPrefix(i18n("Edge detection: ")); 0108 m_d->ui->intEdgeDetectionSize->setToolTip( 0109 i18nc("@info:tooltip", 0110 "Activate for images with vast solid areas. " 0111 "Set the value to the width of the thinnest " 0112 "lines on the image")); 0113 0114 m_d->ui->intRadius->setRange(0, 1000); 0115 m_d->ui->intRadius->setExponentRatio(3.0); 0116 m_d->ui->intRadius->setSuffix(i18n(" px")); 0117 m_d->ui->intRadius->setPrefix(i18n("Gap close hint: ")); 0118 m_d->ui->intRadius->setToolTip( 0119 i18nc("@info:tooltip", 0120 "The mask will try to close non-closed contours " 0121 "if the gap is smaller than \"Gap close hint\" value")); 0122 0123 m_d->ui->intCleanUp->setRange(0, 100); 0124 KisSpinBoxI18nHelper::setText(m_d->ui->intCleanUp, 0125 i18nc("{n} is the number value, % is the percent sign", "Clean up: {n}%")); 0126 m_d->ui->intCleanUp->setToolTip( 0127 i18nc("@info:tooltip", 0128 "The mask will try to remove parts of the key strokes " 0129 "that are placed outside the closed contours. 0% - no effect, 100% - max effect")); 0130 0131 0132 connect(m_d->ui->colorView, SIGNAL(sigIndexSelected(QModelIndex)), this, SLOT(entrySelected(QModelIndex))); 0133 connect(m_d->ui->btnTransparent, SIGNAL(toggled(bool)), this, SLOT(slotMakeTransparent(bool))); 0134 connect(m_d->ui->btnRemove, SIGNAL(clicked()), this, SLOT(slotRemove())); 0135 0136 connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), m_d->ui->btnUpdate, SLOT(setDisabled(bool))); 0137 0138 connect(m_d->ui->btnUpdate, SIGNAL(clicked()), this, SLOT(slotUpdate())); 0139 connect(m_d->ui->chkAutoUpdates, SIGNAL(toggled(bool)), this, SLOT(slotSetAutoUpdates(bool))); 0140 connect(m_d->ui->chkShowKeyStrokes, SIGNAL(toggled(bool)), this, SLOT(slotSetShowKeyStrokes(bool))); 0141 connect(m_d->ui->chkShowOutput, SIGNAL(toggled(bool)), this, SLOT(slotSetShowOutput(bool))); 0142 0143 connect(&m_d->baseNodeChangedCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateNodeProperties())); 0144 0145 m_d->provider = provider; 0146 0147 m_d->colorModel->setColorSet(m_d->colorSet); 0148 0149 const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); 0150 0151 m_d->colorModel->addSwatch(KisSwatch(KoColor(Qt::red, cs), "color1")); 0152 m_d->colorModel->addSwatch(KisSwatch(KoColor(Qt::green, cs), "color2")); 0153 m_d->colorModel->addSwatch(KisSwatch(KoColor(Qt::blue, cs), "color3")); 0154 } 0155 0156 KisToolLazyBrushOptionsWidget::~KisToolLazyBrushOptionsWidget() 0157 { 0158 delete m_d->ui; 0159 m_d->ui = nullptr; 0160 } 0161 0162 void KisToolLazyBrushOptionsWidget::showEvent(QShowEvent *event) 0163 { 0164 QWidget::showEvent(event); 0165 0166 m_d->providerSignals.addConnection( 0167 m_d->provider, SIGNAL(sigNodeChanged(KisNodeSP)), 0168 this, SLOT(slotCurrentNodeChanged(KisNodeSP))); 0169 0170 m_d->providerSignals.addConnection( 0171 m_d->provider, SIGNAL(sigFGColorChanged(KoColor)), 0172 this, SLOT(slotCurrentFgColorChanged(KoColor))); 0173 0174 slotCurrentNodeChanged(m_d->provider->currentNode()); 0175 slotCurrentFgColorChanged(m_d->provider->fgColor()); 0176 } 0177 0178 void KisToolLazyBrushOptionsWidget::hideEvent(QHideEvent *event) 0179 { 0180 QWidget::hideEvent(event); 0181 0182 m_d->providerSignals.clear(); 0183 } 0184 0185 void KisToolLazyBrushOptionsWidget::entrySelected(QModelIndex index) 0186 { 0187 if (!index.isValid()) return; 0188 if (!qvariant_cast<bool>(index.data(KisPaletteModel::CheckSlotRole))) return; 0189 0190 KisSwatch entry = m_d->colorModel->getSwatch(index); 0191 m_d->provider->setFGColor(entry.color()); 0192 0193 int idxInList = m_d->activeMask->keyStrokesColors().colors.indexOf(entry.color()); 0194 0195 if (idxInList != -1) { 0196 const bool transparentChecked = idxInList == m_d->transparentColorIndex; 0197 KisSignalsBlocker b(m_d->ui->btnTransparent); 0198 m_d->ui->btnTransparent->setChecked(transparentChecked); 0199 } 0200 } 0201 0202 void KisToolLazyBrushOptionsWidget::slotCurrentFgColorChanged(const KoColor &color) 0203 { 0204 bool found = false; 0205 0206 QModelIndex candidateIdx = m_d->colorModel->indexForClosest(color); 0207 if (m_d->colorModel->getSwatch(candidateIdx).color() == color) { 0208 found = true; 0209 } 0210 0211 m_d->ui->btnRemove->setEnabled(found); 0212 m_d->ui->btnTransparent->setEnabled(found); 0213 0214 if (!found) { 0215 KisSignalsBlocker b(m_d->ui->btnTransparent); 0216 m_d->ui->btnTransparent->setChecked(false); 0217 } 0218 0219 QModelIndex newIndex = found ? candidateIdx : QModelIndex(); 0220 0221 if (!found) { 0222 m_d->ui->colorView->selectionModel()->clear(); 0223 } 0224 if (newIndex.isValid() && newIndex != m_d->ui->colorView->currentIndex()) { 0225 m_d->ui->colorView->setCurrentIndex(newIndex); 0226 m_d->ui->colorView->update(newIndex); 0227 } 0228 } 0229 0230 void KisToolLazyBrushOptionsWidget::slotColorLabelsChanged() 0231 { 0232 m_d->colorModel->clear(m_d->preferredColumnCount); 0233 m_d->transparentColorIndex = -1; 0234 0235 if (m_d->activeMask) { 0236 KisColorizeMask::KeyStrokeColors colors = m_d->activeMask->keyStrokesColors(); 0237 m_d->transparentColorIndex = colors.transparentIndex; 0238 0239 for (int i = 0; i < colors.colors.size(); i++) { 0240 const QString name = i == m_d->transparentColorIndex ? "transparent" : ""; 0241 m_d->colorModel->addSwatch(KisSwatch(colors.colors[i], name)); 0242 } 0243 } 0244 0245 slotCurrentFgColorChanged(m_d->provider->fgColor()); 0246 } 0247 0248 void KisToolLazyBrushOptionsWidget::slotUpdateNodeProperties() 0249 { 0250 KisSignalsBlocker b1(m_d->ui->chkAutoUpdates, 0251 m_d->ui->btnUpdate, 0252 m_d->ui->chkShowKeyStrokes, 0253 m_d->ui->chkShowOutput); 0254 KisSignalsBlocker b2(m_d->ui->chkUseEdgeDetection, 0255 m_d->ui->intEdgeDetectionSize, 0256 m_d->ui->intRadius, 0257 m_d->ui->intCleanUp, 0258 m_d->ui->chkLimitToDevice); 0259 0260 // not implemented yet! 0261 //m_d->ui->chkAutoUpdates->setEnabled(m_d->activeMask); 0262 m_d->ui->chkAutoUpdates->setEnabled(false); 0263 m_d->ui->chkAutoUpdates->setVisible(false); 0264 0265 bool value = false; 0266 0267 value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, true).toBool(); 0268 m_d->ui->btnUpdate->setEnabled(m_d->activeMask && !m_d->ui->chkAutoUpdates->isChecked() && value); 0269 0270 value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, true).toBool(); 0271 m_d->ui->chkShowKeyStrokes->setEnabled(m_d->activeMask); 0272 m_d->ui->chkShowKeyStrokes->setChecked(value); 0273 0274 value = m_d->activeMask && KisLayerPropertiesIcons::nodeProperty(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, true).toBool(); 0275 m_d->ui->chkShowOutput->setEnabled(m_d->activeMask); 0276 m_d->ui->chkShowOutput->setChecked(value); 0277 0278 m_d->ui->chkUseEdgeDetection->setEnabled(m_d->activeMask); 0279 m_d->ui->chkUseEdgeDetection->setChecked(m_d->activeMask && m_d->activeMask->useEdgeDetection()); 0280 0281 m_d->ui->intEdgeDetectionSize->setEnabled(m_d->activeMask && m_d->ui->chkUseEdgeDetection->isChecked()); 0282 m_d->ui->intEdgeDetectionSize->setValue(m_d->activeMask ? m_d->activeMask->edgeDetectionSize() : 4.0); 0283 m_d->ui->intRadius->setEnabled(m_d->activeMask); 0284 m_d->ui->intRadius->setValue(2 * (m_d->activeMask ? m_d->activeMask->fuzzyRadius() : 15)); 0285 m_d->ui->intCleanUp->setEnabled(m_d->activeMask); 0286 m_d->ui->intCleanUp->setValue(100 * (m_d->activeMask ? m_d->activeMask->cleanUpAmount() : 0.7)); 0287 0288 m_d->ui->chkLimitToDevice->setEnabled(m_d->activeMask); 0289 m_d->ui->chkLimitToDevice->setChecked(m_d->activeMask && m_d->activeMask->limitToDeviceBounds()); 0290 } 0291 0292 void KisToolLazyBrushOptionsWidget::slotCurrentNodeChanged(KisNodeSP node) 0293 { 0294 m_d->maskSignals.clear(); 0295 0296 KisColorizeMask *mask = dynamic_cast<KisColorizeMask*>(node.data()); 0297 m_d->activeMask = mask; 0298 0299 if (m_d->activeMask) { 0300 m_d->maskSignals.addConnection( 0301 m_d->activeMask, SIGNAL(sigKeyStrokesListChanged()), 0302 this, SLOT(slotColorLabelsChanged())); 0303 0304 m_d->maskSignals.addConnection( 0305 m_d->provider->currentImage(), SIGNAL(sigNodeChanged(KisNodeSP)), 0306 this, SLOT(slotUpdateNodeProperties())); 0307 } 0308 0309 slotColorLabelsChanged(); 0310 slotUpdateNodeProperties(); 0311 m_d->ui->colorView->setEnabled(m_d->activeMask); 0312 } 0313 0314 void KisToolLazyBrushOptionsWidget::slotMakeTransparent(bool enableTransparency) 0315 { 0316 KIS_ASSERT_RECOVER_RETURN(m_d->activeMask); 0317 0318 QModelIndex index = m_d->ui->colorView->currentIndex(); 0319 KisSwatch activeSwatch = m_d->colorModel->getSwatch(index); 0320 if (!index.isValid()) return; 0321 0322 QVector<KisSwatchGroup::SwatchInfo> infoList; 0323 Q_FOREACH (const QString &groupName, m_d->colorSet->swatchGroupNames()) { 0324 KisSwatchGroupSP group = m_d->colorSet->getGroup(groupName); 0325 Q_FOREACH (const KisSwatchGroup::SwatchInfo &info, group->infoList()) { 0326 infoList.append(info); 0327 } 0328 } 0329 0330 // We can't rely on the order of the colors returned, so we need to sort them row by row from left to right 0331 std::sort(infoList.begin(), infoList.end(), sortSwatchInfo); 0332 KisColorizeMask::KeyStrokeColors colors; 0333 int i = 0; 0334 for (const KisSwatchGroup::SwatchInfo &info : infoList) { 0335 if (activeSwatch == info.swatch && enableTransparency) { 0336 colors.transparentIndex = i; 0337 } 0338 colors.colors << info.swatch.color(); 0339 i++; 0340 } 0341 0342 m_d->activeMask->setKeyStrokesColors(colors); 0343 } 0344 0345 void KisToolLazyBrushOptionsWidget::slotRemove() 0346 { 0347 KIS_ASSERT_RECOVER_RETURN(m_d->activeMask); 0348 0349 QModelIndex index = m_d->ui->colorView->currentIndex(); 0350 if (!index.isValid()) return; 0351 0352 const KoColor color = m_d->colorModel->getSwatch(index).color(); 0353 m_d->activeMask->removeKeyStroke(color); 0354 } 0355 0356 void KisToolLazyBrushOptionsWidget::slotUpdate() 0357 { 0358 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0359 KisLayerPropertiesIcons::setNodePropertyAutoUndo(m_d->activeMask, KisLayerPropertiesIcons::colorizeNeedsUpdate, false, m_d->provider->currentImage()); 0360 } 0361 0362 void KisToolLazyBrushOptionsWidget::slotSetAutoUpdates(bool value) 0363 { 0364 // not implemented yet! 0365 ENTER_FUNCTION() << ppVar(value); 0366 } 0367 0368 void KisToolLazyBrushOptionsWidget::slotSetShowKeyStrokes(bool value) 0369 { 0370 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0371 KisLayerPropertiesIcons::setNodePropertyAutoUndo(m_d->activeMask, KisLayerPropertiesIcons::colorizeEditKeyStrokes, value, m_d->provider->currentImage()); 0372 } 0373 0374 void KisToolLazyBrushOptionsWidget::slotSetShowOutput(bool value) 0375 { 0376 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0377 KisLayerPropertiesIcons::setNodePropertyAutoUndo(m_d->activeMask, KisLayerPropertiesIcons::colorizeShowColoring, value, m_d->provider->currentImage()); 0378 } 0379 0380 void KisToolLazyBrushOptionsWidget::slotUseEdgeDetectionChanged(bool value) 0381 { 0382 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0383 m_d->activeMask->setUseEdgeDetection(value); 0384 m_d->ui->intEdgeDetectionSize->setEnabled(value); 0385 } 0386 0387 void KisToolLazyBrushOptionsWidget::slotEdgeDetectionSizeChanged(int value) 0388 { 0389 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0390 m_d->activeMask->setEdgeDetectionSize(value); 0391 } 0392 0393 void KisToolLazyBrushOptionsWidget::slotRadiusChanged(int value) 0394 { 0395 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0396 m_d->activeMask->setFuzzyRadius(0.5 * value); 0397 } 0398 0399 void KisToolLazyBrushOptionsWidget::slotCleanUpChanged(int value) 0400 { 0401 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0402 m_d->activeMask->setCleanUpAmount(qreal(value) / 100.0); 0403 } 0404 0405 void KisToolLazyBrushOptionsWidget::slotLimitToDeviceChanged(bool value) 0406 { 0407 KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->activeMask); 0408 m_d->activeMask->setLimitToDeviceBounds(value); 0409 } 0410 0411 bool KisToolLazyBrushOptionsWidget::sortSwatchInfo(const KisSwatchGroup::SwatchInfo &first, const KisSwatchGroup::SwatchInfo &second) 0412 { 0413 if (first.row < second.row) { return true; } 0414 if (first.row > second.row) { return false; } 0415 return first.column < second.column; 0416 }