File indexing completed on 2024-05-19 04:29:05

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2008 Boudewijn Rempt <boud@valdyas.org>
0003  * SPDX-FileCopyrightText: 2011 Silvio Heinrich <plassy@web.de>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "kis_paintop_settings_widget.h"
0009 #include "kis_paintop_option.h"
0010 #include "kis_paintop_options_model.h"
0011 
0012 #include <QHBoxLayout>
0013 #include <QList>
0014 #include <QStackedWidget>
0015 #include <QVBoxLayout>
0016 #include <QPainter>
0017 #include <QStyleOptionViewItem>
0018 #include <QMenu>
0019 #include <QAction>
0020 #include <QShowEvent>
0021 
0022 #include <brushengine/kis_paintop_preset.h>
0023 #include <kis_cmb_composite.h>
0024 #include <kis_categorized_item_delegate.h>
0025 #include <brushengine/kis_locked_properties_server.h>
0026 #include <brushengine/kis_locked_properties_proxy.h>
0027 #include <brushengine/kis_locked_properties.h>
0028 #include <brushengine/kis_paintop_lod_limitations.h>
0029 
0030 #include <lager/constant.hpp>
0031 #include <KisLager.h>
0032 
0033 
0034 struct KisPaintOpSettingsWidget::Private
0035 {
0036     Private()
0037     {
0038     }
0039 
0040     QList<KisPaintOpOption*>    paintOpOptions;
0041     KisCategorizedListView*     optionsList;
0042     KisPaintOpOptionListModel*  model;
0043     QStackedWidget*             optionsStack;
0044     std::optional<lager::reader<KisPaintopLodLimitations>> lodLimitations;
0045 };
0046 
0047 KisPaintOpSettingsWidget::KisPaintOpSettingsWidget(QWidget * parent)
0048         : KisPaintOpConfigWidget(parent)
0049         , m_d(new Private())
0050 {
0051     setObjectName("KisPaintOpPresetsWidget");
0052 
0053     m_d->model       = new KisPaintOpOptionListModel(this);
0054     m_d->optionsList = new KisCategorizedListView(this);
0055     m_d->optionsList->setModel(m_d->model);
0056     m_d->optionsList->setItemDelegate(new KisCategorizedItemDelegate(m_d->optionsList));
0057     m_d->optionsList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
0058     m_d->optionsList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0059 
0060     QSizePolicy policy =  QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
0061     m_d->optionsList->setSizePolicy(policy);
0062 
0063     m_d->optionsList->setMinimumWidth(160); // this should be just big enough to show all of the setting names
0064 
0065     m_d->optionsStack = new QStackedWidget(this);
0066     policy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
0067     m_d->optionsStack->setSizePolicy(policy);
0068 
0069     QHBoxLayout* layout = new QHBoxLayout(this);
0070     layout->addWidget(m_d->optionsList);
0071     layout->addWidget(m_d->optionsStack);
0072 
0073     layout->setStretch(0, 0);
0074     layout->setStretch(1, 1);
0075 
0076     m_saveLockedOption = false;
0077 
0078     connect(m_d->optionsList, SIGNAL(activated(QModelIndex)), this, SLOT(changePage(QModelIndex)));
0079     connect(m_d->optionsList, SIGNAL(clicked(QModelIndex)), this, SLOT(changePage(QModelIndex)));
0080     connect(m_d->optionsList, SIGNAL(rightClickedMenuDropSettingsTriggered()), this, SLOT(slotLockPropertiesDrop()));
0081     connect(m_d->optionsList, SIGNAL(rightClickedMenuSaveSettingsTriggered()), this, SLOT(slotLockPropertiesSave()));
0082     connect(m_d->optionsList, SIGNAL(sigEntryChecked(QModelIndex)), this, SLOT(slotEntryChecked(QModelIndex)));
0083     connect (m_d->optionsList, SIGNAL(lockAreaTriggered(QModelIndex)), this, SLOT(lockProperties(QModelIndex)));
0084 
0085     // allow the list viewport to be responsive to input release events
0086     m_d->optionsList->viewport()->installEventFilter(this);
0087 }
0088 
0089 
0090 KisPaintOpSettingsWidget::~KisPaintOpSettingsWidget()
0091 {
0092     qDeleteAll(m_d->paintOpOptions);
0093     delete m_d;
0094 }
0095 
0096 void KisPaintOpSettingsWidget::addPaintOpOption(KisPaintOpOption *option)
0097 {
0098     addPaintOpOption(option, option->category());
0099 }
0100 
0101 void KisPaintOpSettingsWidget::addPaintOpOption(KisPaintOpOption *option, KisPaintOpOption::PaintopCategory category)
0102 {
0103     addPaintOpOption(option, m_d->model->categoryName(category));
0104 }
0105 
0106 void KisPaintOpSettingsWidget::addPaintOpOption(KisPaintOpOption *option, QString category)
0107 {
0108     if (!option->configurationPage()) return;
0109     m_d->model->addPaintOpOption(option, m_d->optionsStack->count(), option->label(), category);
0110     connect(option, SIGNAL(sigSettingChanged()), SIGNAL(sigConfigurationItemChanged()));
0111     m_d->optionsStack->addWidget(option->configurationPage());
0112     m_d->paintOpOptions << option;
0113 
0114     m_d->lodLimitations =
0115         kislager::fold_optional_cursors(std::bit_or{}, m_d->lodLimitations,
0116                                       option->effectiveLodLimitations());
0117 }
0118 
0119 void KisPaintOpSettingsWidget::setConfiguration(const KisPropertiesConfigurationSP  config)
0120 {
0121     Q_ASSERT(!config->getString("paintop").isEmpty());
0122     KisLockedPropertiesProxySP propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(config);
0123     int indexcount = 0;
0124     Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) {
0125         option->startReadOptionSetting(propertiesProxy);
0126 
0127         KisLockedPropertiesServer::instance()->setPropertiesFromLocked(false);
0128         KisOptionInfo info;
0129         info.option = option;
0130         info.index = indexcount;
0131 
0132         m_d->model->categoriesMapper()->itemFromRow(m_d->model->indexOf(info).row())->setLocked(option->isLocked());
0133         m_d->model->categoriesMapper()->itemFromRow(m_d->model->indexOf(info).row())->setLockable(true);
0134         m_d->model->signalDataChanged(m_d->model->indexOf(info));
0135         indexcount++;
0136     }
0137 }
0138 
0139 void KisPaintOpSettingsWidget::writeConfiguration(KisPropertiesConfigurationSP config) const
0140 {
0141     KisLockedPropertiesProxySP propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(config);
0142     Q_FOREACH (const KisPaintOpOption* option, m_d->paintOpOptions) {
0143         option->startWriteOptionSetting(propertiesProxy);
0144     }
0145 }
0146 
0147 KisPaintopLodLimitations KisPaintOpSettingsWidget::lodLimitations() const
0148 {
0149     KisPaintopLodLimitations l;
0150 
0151     Q_FOREACH (const KisPaintOpOption* option, m_d->paintOpOptions) {
0152         if (option->isCheckable() && !option->isChecked()) continue;
0153         option->lodLimitations(&l);
0154     }
0155 
0156     return l;
0157 }
0158 
0159 lager::reader<KisPaintopLodLimitations> KisPaintOpSettingsWidget::lodLimitationsReader() const
0160 {
0161     return m_d->lodLimitations.value_or(lager::make_constant(KisPaintopLodLimitations()));
0162 }
0163 
0164 lager::reader<qreal> KisPaintOpSettingsWidget::effectiveBrushSize() const
0165 {
0166     return lager::make_constant(1.0);
0167 }
0168 
0169 void KisPaintOpSettingsWidget::setImage(KisImageWSP image)
0170 {
0171     KisPaintOpConfigWidget::setImage(image);
0172 
0173     Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) {
0174         option->setImage(image);
0175     }
0176 }
0177 
0178 void KisPaintOpSettingsWidget::setNode(KisNodeWSP node)
0179 {
0180     KisPaintOpConfigWidget::setNode(node);
0181 
0182     Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) {
0183         option->setNode(node);
0184     }
0185 }
0186 
0187 void KisPaintOpSettingsWidget::setResourcesInterface(KisResourcesInterfaceSP resourcesInterface)
0188 {
0189     KisPaintOpConfigWidget::setResourcesInterface(resourcesInterface);
0190 
0191     Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) {
0192         option->setResourcesInterface(resourcesInterface);
0193     }
0194 }
0195 
0196 void KisPaintOpSettingsWidget::setCanvasResourcesInterface(KoCanvasResourcesInterfaceSP canvasResourcesInterface)
0197 {
0198     KisPaintOpConfigWidget::setCanvasResourcesInterface(canvasResourcesInterface);
0199 
0200     Q_FOREACH (KisPaintOpOption* option, m_d->paintOpOptions) {
0201         option->setCanvasResourcesInterface(canvasResourcesInterface);
0202     }
0203 }
0204 
0205 void KisPaintOpSettingsWidget::changePage(const QModelIndex& index)
0206 {
0207     KisOptionInfo info;
0208     QPalette palette;
0209     palette.setColor(QPalette::Base, QColor(255,200,200));
0210     palette.setColor(QPalette::Text, Qt::black);
0211 
0212     if(m_d->model->entryAt(info, index)) {
0213         m_d->optionsStack->setCurrentIndex(info.index);
0214     }
0215 
0216     notifyPageChanged();
0217 }
0218 
0219 void KisPaintOpSettingsWidget::notifyPageChanged()
0220 {
0221 }
0222 
0223 void KisPaintOpSettingsWidget::lockProperties(const QModelIndex& index)
0224 {
0225     KisOptionInfo info;
0226     if (m_d->model->entryAt(info, index)) {
0227         m_d->optionsList->setCurrentIndex(index);
0228         KisPropertiesConfigurationSP p = new KisPropertiesConfiguration();
0229         info.option->startWriteOptionSetting(p);
0230 
0231         if (!info.option->isLocked()){
0232             KisLockedPropertiesServer::instance()->addToLockedProperties(p);
0233             info.option->setLocked(true);
0234             m_d->model->categoriesMapper()->itemFromRow(index.row())->setLocked(true);
0235             m_d->model->signalDataChanged(index);
0236 
0237         }
0238         else {
0239             KisLockedPropertiesServer::instance()->removeFromLockedProperties(p);
0240             info.option->setLocked(false);
0241             m_d->model->categoriesMapper()->itemFromRow(index.row())->setLocked(false);
0242             m_d->model->signalDataChanged(index);
0243 
0244             if (m_saveLockedOption){
0245                 emit sigSaveLockedConfig(p);
0246             }
0247             else {
0248                 emit sigDropLockedConfig(p);
0249             }
0250             m_saveLockedOption = false;
0251         }
0252     }
0253 }
0254 void KisPaintOpSettingsWidget::slotLockPropertiesDrop()
0255 {
0256     m_saveLockedOption = false;
0257     lockProperties(m_d->optionsList->currentIndex());
0258 }
0259 void KisPaintOpSettingsWidget::slotLockPropertiesSave()
0260 {
0261     m_saveLockedOption = true;
0262     lockProperties(m_d->optionsList->currentIndex());
0263 }
0264 void KisPaintOpSettingsWidget::slotEntryChecked(const QModelIndex &index)
0265 {
0266     Q_UNUSED(index);
0267     emit sigConfigurationItemChanged();
0268 }
0269 
0270 bool KisPaintOpSettingsWidget::eventFilter(QObject *obj, QEvent *event)
0271 {
0272     if (event->type() == (QEvent::MouseButtonRelease) || event->type() == QEvent::TabletRelease) {
0273         // bit of a hack to properly work with the KisCategorizedListView type:
0274         // get the mouse position and utilize indexAt() instead of currentIndex() to
0275         // ensure the pages don't swap back and forth if the user repeatedly clicks
0276         // another option's checkbox too quickly
0277         const QMouseEvent* mevent = static_cast<const QMouseEvent*>(event);
0278         QModelIndex index = m_d->optionsList->indexAt(mevent->pos());
0279         changePage(index);
0280         event->accept();
0281     }
0282 
0283     return QObject::eventFilter(obj, event);
0284 }