File indexing completed on 2024-12-22 04:13:09
0001 /* 0002 * kis_cmb_composite.cc - part of KImageShop/Krayon/Krita 0003 * 0004 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt (boud@valdyas.org) 0005 * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de> 0006 * 0007 * SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kis_cmb_composite.h" 0011 0012 #include <KoCompositeOp.h> 0013 #include <KoCompositeOpRegistry.h> 0014 0015 #include "kis_composite_ops_model.h" 0016 #include "kis_categorized_item_delegate.h" 0017 #include <kis_action.h> 0018 #include <QWheelEvent> 0019 #include "kis_action_manager.h" 0020 0021 ////////////////////////////////////////////////////////////////////////////////////////// 0022 // ---- KisCompositeOpListWidget ------------------------------------------------------ // 0023 0024 KisCompositeOpListWidget::KisCompositeOpListWidget(QWidget* parent): 0025 KisCategorizedListView(parent), 0026 m_model(new KisSortedCompositeOpListModel(false, this)) 0027 { 0028 setModel(m_model); 0029 setItemDelegate(new KisCategorizedItemDelegate(this)); 0030 } 0031 0032 KisCompositeOpListWidget::~KisCompositeOpListWidget() 0033 { 0034 } 0035 0036 KoID KisCompositeOpListWidget::selectedCompositeOp() const { 0037 KoID op; 0038 0039 if (m_model->entryAt(op, currentIndex())) { 0040 return op; 0041 } 0042 0043 return KoCompositeOpRegistry::instance().getDefaultCompositeOp(); 0044 } 0045 0046 void KisCompositeOpListWidget::setCompositeOp(const KoID &id) 0047 { 0048 const QModelIndex index = m_model->indexOf(id); 0049 if (index.isValid()) { 0050 setCurrentIndex(index); 0051 } else { 0052 qWarning() << "KisCompositeOpListWidget::setCompositeOp: ailed to find index for blendmode" << ppVar(id); 0053 } 0054 } 0055 0056 ////////////////////////////////////////////////////////////////////////////////////////// 0057 // ---- KisCompositeOpComboBox -------------------------------------------------------- // 0058 0059 KisCompositeOpComboBox::KisCompositeOpComboBox(QWidget* parent) 0060 : KisCompositeOpComboBox(false, parent) 0061 { 0062 } 0063 0064 KisCompositeOpComboBox::KisCompositeOpComboBox(bool limitToLayerStyles, QWidget* parent) 0065 : KisSqueezedComboBox(parent), 0066 m_model(new KisSortedCompositeOpListModel(limitToLayerStyles, this)), 0067 m_allowToHidePopup(true) 0068 { 0069 m_view = new KisCategorizedListView(); 0070 m_view->setCompositeBoxControl(true); 0071 0072 setMaxVisibleItems(100); 0073 setSizeAdjustPolicy(AdjustToContents); 0074 m_view->setResizeMode(QListView::Adjust); 0075 0076 setToolTip(i18n("Blending Mode")); 0077 0078 setModel(m_model); 0079 setView(m_view); 0080 setItemDelegate(new KisCategorizedItemDelegate(this)); 0081 0082 connect(m_view, SIGNAL(sigCategoryToggled(QModelIndex,bool)), SLOT(slotCategoryToggled(QModelIndex,bool))); 0083 connect(m_view, SIGNAL(sigEntryChecked(QModelIndex)), SLOT(slotEntryChecked(QModelIndex))); 0084 0085 selectCompositeOp(KoCompositeOpRegistry::instance().getDefaultCompositeOp()); 0086 } 0087 0088 KisCompositeOpComboBox::~KisCompositeOpComboBox() 0089 { 0090 delete m_view; 0091 } 0092 0093 void KisCompositeOpComboBox::connectBlendmodeActions(KisActionManager *manager) 0094 { 0095 KisAction *action = 0; 0096 0097 action = manager->createAction("Next Blending Mode"); 0098 connect(action, SIGNAL(triggered()), SLOT(slotNextBlendingMode())); 0099 0100 action = manager->createAction("Previous Blending Mode"); 0101 connect(action, SIGNAL(triggered()), SLOT(slotPreviousBlendingMode())); 0102 0103 action = manager->createAction("Select Normal Blending Mode"); 0104 connect(action, SIGNAL(triggered()), SLOT(slotNormal())); 0105 0106 action = manager->createAction("Select Dissolve Blending Mode"); 0107 connect(action, SIGNAL(triggered()), SLOT(slotDissolve())); 0108 0109 action = manager->createAction("Select Behind Blending Mode"); 0110 connect(action, SIGNAL(triggered()), SLOT(slotBehind())); 0111 0112 action = manager->createAction("Select Clear Blending Mode"); 0113 connect(action, SIGNAL(triggered()), SLOT(slotClear())); 0114 0115 action = manager->createAction("Select Darken Blending Mode"); 0116 connect(action, SIGNAL(triggered()), SLOT(slotDarken())); 0117 0118 action = manager->createAction("Select Multiply Blending Mode"); 0119 connect(action, SIGNAL(triggered()), SLOT(slotMultiply())); 0120 0121 action = manager->createAction("Select Color Burn Blending Mode"); 0122 connect(action, SIGNAL(triggered()), SLOT(slotColorBurn())); 0123 0124 action = manager->createAction("Select Linear Burn Blending Mode"); 0125 connect(action, SIGNAL(triggered()), SLOT(slotLinearBurn())); 0126 0127 action = manager->createAction("Select Lighten Blending Mode"); 0128 connect(action, SIGNAL(triggered()), SLOT(slotLighten())); 0129 0130 action = manager->createAction("Select Screen Blending Mode"); 0131 connect(action, SIGNAL(triggered()), SLOT(slotScreen())); 0132 0133 action = manager->createAction("Select Color Dodge Blending Mode"); 0134 connect(action, SIGNAL(triggered()), SLOT(slotColorDodge())); 0135 0136 action = manager->createAction("Select Linear Dodge Blending Mode"); 0137 connect(action, SIGNAL(triggered()), SLOT(slotLinearDodge())); 0138 0139 action = manager->createAction("Select Overlay Blending Mode"); 0140 connect(action, SIGNAL(triggered()), SLOT(slotOverlay())); 0141 0142 action = manager->createAction("Select Hard Overlay Blending Mode"); 0143 connect(action, SIGNAL(triggered()), SLOT(slotHardOverlay())); 0144 0145 action = manager->createAction("Select Soft Light Blending Mode"); 0146 connect(action, SIGNAL(triggered()), SLOT(slotSoftLight())); 0147 0148 action = manager->createAction("Select Hard Light Blending Mode"); 0149 connect(action, SIGNAL(triggered()), SLOT(slotHardLight())); 0150 0151 action = manager->createAction("Select Vivid Light Blending Mode"); 0152 connect(action, SIGNAL(triggered()), SLOT(slotVividLight())); 0153 0154 action = manager->createAction("Select Linear Light Blending Mode"); 0155 connect(action, SIGNAL(triggered()), SLOT(slotLinearLight())); 0156 0157 action = manager->createAction("Select Pin Light Blending Mode"); 0158 connect(action, SIGNAL(triggered()), SLOT(slotPinLight())); 0159 0160 action = manager->createAction("Select Hard Mix Blending Mode"); 0161 connect(action, SIGNAL(triggered()), SLOT(slotHardMix())); 0162 0163 action = manager->createAction("Select Difference Blending Mode"); 0164 connect(action, SIGNAL(triggered()), SLOT(slotDifference())); 0165 0166 action = manager->createAction("Select Exclusion Blending Mode"); 0167 connect(action, SIGNAL(triggered()), SLOT(slotExclusion())); 0168 0169 action = manager->createAction("Select Hue Blending Mode"); 0170 connect(action, SIGNAL(triggered()), SLOT(slotHue())); 0171 0172 action = manager->createAction("Select Saturation Blending Mode"); 0173 connect(action, SIGNAL(triggered()), SLOT(slotSaturation())); 0174 0175 action = manager->createAction("Select Color Blending Mode"); 0176 connect(action, SIGNAL(triggered()), SLOT(slotColor())); 0177 0178 action = manager->createAction("Select Luminosity Blending Mode"); 0179 connect(action, SIGNAL(triggered()), SLOT(slotLuminosity())); 0180 } 0181 0182 void KisCompositeOpComboBox::validate(const KoColorSpace *cs) { 0183 m_model->validate(cs); 0184 } 0185 0186 void KisCompositeOpComboBox::selectCompositeOp(const KoID &op) { 0187 KoID currentOp; 0188 if (m_model->entryAt(currentOp, m_model->index(currentIndex(), 0)) && 0189 currentOp == op) { 0190 0191 return; 0192 } 0193 0194 QModelIndex index = m_model->indexOf(op); 0195 0196 setCurrentIndex(index.row()); 0197 emit activated(index.row()); 0198 emit activated(op.name()); 0199 } 0200 0201 KoID KisCompositeOpComboBox::selectedCompositeOp() const { 0202 KoID op; 0203 0204 if (m_model->entryAt(op, m_model->index(currentIndex(), 0))) { 0205 return op; 0206 } 0207 return KoCompositeOpRegistry::instance().getDefaultCompositeOp(); 0208 } 0209 0210 void KisCompositeOpComboBox::slotCategoryToggled(const QModelIndex& index, bool toggled) 0211 { 0212 Q_UNUSED(index); 0213 Q_UNUSED(toggled); 0214 0215 //NOTE: this will (should) fit the size of the 0216 // popup widget to the view 0217 // don't know if this is expected behaviour 0218 // on all supported platforms. 0219 // There is nothing written about this in the docs. 0220 showPopup(); 0221 } 0222 0223 void KisCompositeOpComboBox::slotEntryChecked(const QModelIndex& index) 0224 { 0225 Q_UNUSED(index); 0226 m_allowToHidePopup = false; 0227 } 0228 0229 void KisCompositeOpComboBox::hidePopup() 0230 { 0231 if (m_allowToHidePopup) { 0232 QComboBox::hidePopup(); 0233 } 0234 else { 0235 QComboBox::showPopup(); 0236 } 0237 0238 m_allowToHidePopup = true; 0239 } 0240 0241 void KisCompositeOpComboBox::slotNextBlendingMode() 0242 { 0243 selectNeighbouringBlendMode(true); 0244 } 0245 0246 void KisCompositeOpComboBox::slotPreviousBlendingMode() 0247 { 0248 selectNeighbouringBlendMode(false); 0249 } 0250 0251 void KisCompositeOpComboBox::slotNormal() 0252 { 0253 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVER)); 0254 } 0255 0256 void KisCompositeOpComboBox::slotDissolve() 0257 { 0258 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DISSOLVE)); 0259 } 0260 0261 void KisCompositeOpComboBox::slotBehind() 0262 { 0263 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BEHIND)); 0264 } 0265 0266 void KisCompositeOpComboBox::slotClear() 0267 { 0268 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_CLEAR)); 0269 } 0270 0271 void KisCompositeOpComboBox::slotDarken() 0272 { 0273 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DARKEN)); 0274 } 0275 0276 void KisCompositeOpComboBox::slotMultiply() 0277 { 0278 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_MULT)); 0279 } 0280 0281 void KisCompositeOpComboBox::slotColorBurn() 0282 { 0283 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_BURN)); 0284 } 0285 0286 void KisCompositeOpComboBox::slotLinearBurn() 0287 { 0288 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_BURN)); 0289 } 0290 0291 void KisCompositeOpComboBox::slotLighten() 0292 { 0293 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LIGHTEN)); 0294 } 0295 0296 void KisCompositeOpComboBox::slotScreen() 0297 { 0298 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SCREEN)); 0299 } 0300 0301 void KisCompositeOpComboBox::slotColorDodge() 0302 { 0303 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DODGE)); 0304 } 0305 0306 void KisCompositeOpComboBox::slotLinearDodge() 0307 { 0308 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_DODGE)); 0309 } 0310 0311 void KisCompositeOpComboBox::slotOverlay() 0312 { 0313 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_OVERLAY)); 0314 } 0315 0316 void KisCompositeOpComboBox::slotHardOverlay() 0317 { 0318 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_OVERLAY)); 0319 } 0320 0321 void KisCompositeOpComboBox::slotSoftLight() 0322 { 0323 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SOFT_LIGHT_PHOTOSHOP)); 0324 } 0325 0326 void KisCompositeOpComboBox::slotHardLight() 0327 { 0328 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_LIGHT)); 0329 } 0330 0331 void KisCompositeOpComboBox::slotVividLight() 0332 { 0333 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_VIVID_LIGHT)); 0334 } 0335 0336 void KisCompositeOpComboBox::slotLinearLight() 0337 { 0338 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LINEAR_LIGHT)); 0339 } 0340 0341 void KisCompositeOpComboBox::slotPinLight() 0342 { 0343 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_PIN_LIGHT)); 0344 } 0345 0346 void KisCompositeOpComboBox::slotHardMix() 0347 { 0348 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HARD_MIX_PHOTOSHOP)); 0349 } 0350 0351 void KisCompositeOpComboBox::slotDifference() 0352 { 0353 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_DIFF)); 0354 } 0355 0356 void KisCompositeOpComboBox::slotExclusion() 0357 { 0358 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_EXCLUSION)); 0359 } 0360 0361 void KisCompositeOpComboBox::slotHue() 0362 { 0363 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_HUE)); 0364 } 0365 0366 void KisCompositeOpComboBox::slotSaturation() 0367 { 0368 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_SATURATION)); 0369 } 0370 0371 void KisCompositeOpComboBox::slotColor() 0372 { 0373 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_COLOR)); 0374 } 0375 0376 void KisCompositeOpComboBox::slotLuminosity() 0377 { 0378 selectCompositeOp(KoCompositeOpRegistry::instance().getKoID(COMPOSITE_LUMINIZE)); 0379 } 0380 0381 void KisCompositeOpComboBox::selectNeighbouringBlendMode(bool down) 0382 { 0383 const int rowCount = count(); 0384 int newIndex = currentIndex(); 0385 0386 QAbstractItemModel *model = this->model(); 0387 KoID op; 0388 0389 if (!down) { 0390 newIndex--; 0391 while ((newIndex >= 0) && 0392 (!(model->flags(model->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) || 0393 !m_model->entryAt(op, m_model->index(newIndex, modelColumn())))) 0394 0395 newIndex--; 0396 } else { 0397 newIndex++; 0398 while (newIndex < rowCount && 0399 (!(model->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) || 0400 !m_model->entryAt(op, m_model->index(newIndex, modelColumn())))) 0401 0402 newIndex++; 0403 } 0404 0405 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) { 0406 setCurrentIndex(newIndex); 0407 0408 emit activated(newIndex); 0409 if (m_model->entryAt(op, m_model->index(newIndex, 0))) { 0410 emit activated(op.name()); 0411 } 0412 } 0413 } 0414 0415 void KisCompositeOpComboBox::wheelEvent(QWheelEvent *e) 0416 { 0417 /** 0418 * This code is a copy of QComboBox::wheelEvent. It does the same thing, 0419 * except that it skips "Category" items, by checking m_model->entryAt() 0420 * on each step. 0421 */ 0422 0423 QStyleOptionComboBox opt; 0424 initStyleOption(&opt); 0425 0426 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) 0427 if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt, this)) { 0428 #else 0429 if (1) { 0430 #endif 0431 0432 if (e->delta() != 0) { 0433 selectNeighbouringBlendMode(e->delta() < 0); 0434 } 0435 0436 e->accept(); 0437 } else { 0438 KisSqueezedComboBox::wheelEvent(e); 0439 } 0440 } 0441 0442 void KisCompositeOpComboBox::keyPressEvent(QKeyEvent *e) 0443 { 0444 /** 0445 * This code is a copy of QComboBox::keyPressEvent. It does the same thing, 0446 * except that it skips "Category" items, by checking m_model->entryAt() 0447 * on each step. 0448 */ 0449 0450 enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast}; 0451 0452 Move move = NoMove; 0453 int newIndex = currentIndex(); 0454 switch (e->key()) { 0455 case Qt::Key_Up: 0456 if (e->modifiers() & Qt::ControlModifier) 0457 break; // pass to line edit for auto completion 0458 Q_FALLTHROUGH(); 0459 case Qt::Key_PageUp: 0460 move = MoveUp; 0461 break; 0462 case Qt::Key_Down: 0463 if (e->modifiers() & Qt::AltModifier) { 0464 showPopup(); 0465 return; 0466 } else if (e->modifiers() & Qt::ControlModifier) 0467 break; // pass to line edit for auto completion 0468 Q_FALLTHROUGH(); 0469 case Qt::Key_PageDown: 0470 move = MoveDown; 0471 break; 0472 case Qt::Key_Home: 0473 move = MoveFirst; 0474 break; 0475 case Qt::Key_End: 0476 move = MoveLast; 0477 break; 0478 case Qt::Key_F4: 0479 if (!e->modifiers()) { 0480 showPopup(); 0481 return; 0482 } 0483 break; 0484 case Qt::Key_Space: 0485 showPopup(); 0486 return; 0487 default: 0488 break; 0489 } 0490 0491 const int rowCount = count(); 0492 0493 if (move != NoMove) { 0494 KoID op; 0495 0496 e->accept(); 0497 switch (move) { 0498 case MoveFirst: 0499 newIndex = -1; 0500 Q_FALLTHROUGH(); 0501 case MoveDown: 0502 newIndex++; 0503 while (newIndex < rowCount && 0504 (!(model()->index(newIndex, modelColumn(), rootModelIndex()).flags() & Qt::ItemIsEnabled) || 0505 !m_model->entryAt(op, m_model->index(newIndex, modelColumn())))) 0506 newIndex++; 0507 break; 0508 case MoveLast: 0509 newIndex = rowCount; 0510 Q_FALLTHROUGH(); 0511 case MoveUp: 0512 newIndex--; 0513 while ((newIndex >= 0) && 0514 (!(model()->flags(model()->index(newIndex, modelColumn(), rootModelIndex())) & Qt::ItemIsEnabled) || 0515 !m_model->entryAt(op, m_model->index(newIndex, modelColumn())))) 0516 newIndex--; 0517 break; 0518 default: 0519 e->ignore(); 0520 break; 0521 } 0522 0523 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) { 0524 setCurrentIndex(newIndex); 0525 emit activated(newIndex); 0526 0527 if (m_model->entryAt(op, m_model->index(newIndex, 0))) { 0528 emit activated(op.name()); 0529 } 0530 } 0531 } else { 0532 KisSqueezedComboBox::keyPressEvent(e); 0533 } 0534 } 0535 0536 KisLayerStyleCompositeOpComboBox::KisLayerStyleCompositeOpComboBox(QWidget* parent) 0537 : KisCompositeOpComboBox(true, parent) 0538 { 0539 }