File indexing completed on 2024-05-26 04:29:56

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_brush_hud.h"
0008 
0009 #include <QGuiApplication>
0010 #include <QVBoxLayout>
0011 #include <QHBoxLayout>
0012 #include <QPointer>
0013 #include <QLabel>
0014 #include <QPainter>
0015 #include <QPaintEvent>
0016 #include <QScrollArea>
0017 #include <QEvent>
0018 #include <QToolButton>
0019 #include <QAction>
0020 
0021 #include "kis_uniform_paintop_property.h"
0022 #include "kis_slider_based_paintop_property.h"
0023 #include "kis_uniform_paintop_property_widget.h"
0024 #include "kis_canvas_resource_provider.h"
0025 #include "kis_paintop_preset.h"
0026 #include "kis_paintop_settings.h"
0027 #include "kis_signal_auto_connection.h"
0028 #include "KisPaintOpPresetUpdateProxy.h"
0029 #include "kis_icon_utils.h"
0030 #include "kis_dlg_brush_hud_config.h"
0031 #include "kis_brush_hud_properties_config.h"
0032 #include "kis_elided_label.h"
0033 
0034 #include "kis_canvas2.h"
0035 #include "KisViewManager.h"
0036 #include "kactioncollection.h"
0037 
0038 #include "kis_debug.h"
0039 
0040 
0041 struct KisBrushHud::Private
0042 {
0043     QPointer<KisElidedLabel> lblPresetName;
0044     QPointer<QLabel> lblPresetIcon;
0045     QPointer<QWidget> wdgProperties;
0046     QPointer<QScrollArea> wdgPropertiesArea;
0047     QPointer<QVBoxLayout> propertiesLayout;
0048     QPointer<QToolButton> btnReloadPreset;
0049     QPointer<QToolButton> btnConfigure;
0050 
0051     KisCanvasResourceProvider *provider;
0052 
0053     KisSignalAutoConnectionsStore connections;
0054     KisSignalAutoConnectionsStore presetConnections;
0055 
0056     KisPaintOpPresetSP currentPreset;
0057 };
0058 
0059 KisBrushHud::KisBrushHud(KisCanvasResourceProvider *provider, QWidget *parent)
0060     : QWidget(parent, Qt::FramelessWindowHint),
0061       m_d(new Private)
0062 {
0063     m_d->provider = provider;
0064 
0065     QVBoxLayout *layout = new QVBoxLayout(this);
0066 
0067     QHBoxLayout *labelLayout = new QHBoxLayout();
0068     m_d->lblPresetIcon = new QLabel(this);
0069     const QSize iconSize = QSize(22,22);
0070     m_d->lblPresetIcon->setMinimumSize(iconSize);
0071     m_d->lblPresetIcon->setMaximumSize(iconSize);
0072     m_d->lblPresetIcon->setScaledContents(true);
0073 
0074     m_d->lblPresetName = new KisElidedLabel("<Preset Name>", Qt::ElideMiddle, this);
0075 
0076     m_d->btnReloadPreset = new QToolButton(this);
0077     m_d->btnReloadPreset->setAutoRaise(true);
0078     m_d->btnReloadPreset->setToolTip(i18n("Reload Original Preset"));
0079 
0080     m_d->btnConfigure = new QToolButton(this);
0081     m_d->btnConfigure->setAutoRaise(true);
0082     m_d->btnConfigure->setToolTip(i18n("Configure the on-canvas brush editor"));
0083 
0084     connect(m_d->btnReloadPreset, SIGNAL(clicked()), SLOT(slotReloadPreset()));
0085     connect(m_d->btnConfigure, SIGNAL(clicked()), SLOT(slotConfigBrushHud()));
0086 
0087     labelLayout->addWidget(m_d->lblPresetIcon);
0088     labelLayout->addWidget(m_d->lblPresetName);
0089     labelLayout->addWidget(m_d->btnReloadPreset);
0090     labelLayout->addWidget(m_d->btnConfigure);
0091 
0092     layout->addLayout(labelLayout);
0093 
0094     m_d->wdgPropertiesArea = new QScrollArea(this);
0095     m_d->wdgPropertiesArea->setAlignment(Qt::AlignLeft | Qt::AlignTop);
0096     m_d->wdgPropertiesArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0097 
0098     m_d->wdgPropertiesArea->setWidgetResizable(true);
0099 
0100     m_d->wdgProperties = new QWidget(this);
0101     m_d->propertiesLayout = new QVBoxLayout(m_d->wdgProperties);
0102     m_d->propertiesLayout->setSpacing(0);
0103     m_d->propertiesLayout->setContentsMargins(0, 0, 22, 0);
0104     m_d->propertiesLayout->setSizeConstraint(QLayout::SetMinimumSize);
0105 
0106     // not adding any widgets until explicitly requested
0107 
0108     m_d->wdgPropertiesArea->setWidget(m_d->wdgProperties);
0109     layout->addWidget(m_d->wdgPropertiesArea);
0110 
0111     // unfortunately the sizeHint() function of QScrollArea is pretty broken
0112     // and it would add another event loop iteration to react to it anyway,
0113     // so let's just catch LayoutRequest events from the properties widget directly
0114     m_d->wdgProperties->installEventFilter(this);
0115 
0116     updateIcons();
0117 
0118     setCursor(Qt::ArrowCursor);
0119 
0120     // Prevent tablet events from being captured by the canvas
0121     setAttribute(Qt::WA_NoMousePropagation, true);
0122 }
0123 
0124 
0125 
0126 KisBrushHud::~KisBrushHud()
0127 {
0128 }
0129 
0130 void KisBrushHud::updateIcons()
0131 {
0132     this->setPalette(qApp->palette());
0133     for(int i=0; i<this->children().size(); i++) {
0134         QWidget *w = qobject_cast<QWidget*>(this->children().at(i));
0135         if (w) {
0136             w->setPalette(qApp->palette());
0137         }
0138     }
0139     for(int i=0; i<m_d->wdgProperties->children().size(); i++) {
0140         KisUniformPaintOpPropertyWidget *w = qobject_cast<KisUniformPaintOpPropertyWidget*>(m_d->wdgProperties->children().at(i));
0141         if (w) {
0142             w->slotThemeChanged(qApp->palette());
0143         }
0144     }
0145     m_d->btnReloadPreset->setIcon(KisIconUtils::loadIcon("reload-preset-16"));
0146     m_d->btnConfigure->setIcon(KisIconUtils::loadIcon("applications-system"));
0147 }
0148 
0149 void KisBrushHud::slotReloadProperties()
0150 {
0151     m_d->presetConnections.clear();
0152     clearProperties();
0153     updateProperties();
0154 }
0155 
0156 void KisBrushHud::clearProperties() const
0157 {
0158     while (m_d->propertiesLayout->count()) {
0159         QLayoutItem *item = m_d->propertiesLayout->takeAt(0);
0160 
0161         QWidget *w = item->widget();
0162         if (w) {
0163             w->deleteLater();
0164         }
0165 
0166         delete item;
0167     }
0168 
0169     m_d->currentPreset.clear();
0170 }
0171 
0172 void KisBrushHud::updateProperties()
0173 {
0174     KisPaintOpPresetSP preset = m_d->provider->currentPreset();
0175 
0176     if (preset == m_d->currentPreset) return;
0177 
0178     m_d->presetConnections.clear();
0179     clearProperties();
0180 
0181     m_d->currentPreset = preset;
0182     m_d->presetConnections.addConnection(
0183         m_d->currentPreset->updateProxy(), SIGNAL(sigUniformPropertiesChanged()),
0184         this, SLOT(slotReloadProperties()));
0185 
0186     m_d->lblPresetIcon->setPixmap(QPixmap::fromImage(preset->image()));
0187     m_d->lblPresetName->setLongText(preset->name());
0188 
0189     QList<KisUniformPaintOpPropertySP> properties;
0190 
0191     {
0192         QList<KisUniformPaintOpPropertySP> allProperties = preset->uniformProperties();
0193         QList<KisUniformPaintOpPropertySP> discardedProperties;
0194 
0195         KisBrushHudPropertiesConfig cfg;
0196         cfg.filterProperties(preset->paintOp().id(),
0197                              allProperties,
0198                              &properties,
0199                              &discardedProperties);
0200     }
0201 
0202     Q_FOREACH(auto property, properties) {
0203         QWidget *w = 0;
0204 
0205         if (!property->isVisible()) continue;
0206 
0207         if (property->type() == KisUniformPaintOpProperty::Int) {
0208             w = new KisUniformPaintOpPropertyIntSlider(property, m_d->wdgProperties);
0209         } else if (property->type() == KisUniformPaintOpProperty::Double) {
0210             w = new KisUniformPaintOpPropertyDoubleSlider(property, m_d->wdgProperties);
0211         } else if (property->type() == KisUniformPaintOpProperty::Bool) {
0212             w = new KisUniformPaintOpPropertyCheckBox(property, m_d->wdgProperties);
0213         } else if (property->type() == KisUniformPaintOpProperty::Combo) {
0214             w = new KisUniformPaintOpPropertyComboBox(property, m_d->wdgProperties);
0215         }
0216 
0217         if (w) {
0218             w->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
0219             m_d->propertiesLayout->addWidget(w);
0220         }
0221     }
0222 
0223     m_d->propertiesLayout->addStretch();
0224 }
0225 
0226 void KisBrushHud::showEvent(QShowEvent *event)
0227 {
0228     m_d->connections.clear();
0229     m_d->connections.addUniqueConnection(
0230         m_d->provider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
0231         this, SLOT(slotCanvasResourceChanged(int,QVariant)));
0232 
0233     updateProperties();
0234 
0235     QWidget::showEvent(event);
0236 }
0237 
0238 void KisBrushHud::hideEvent(QHideEvent *event)
0239 {
0240     m_d->connections.clear();
0241     QWidget::hideEvent(event);
0242 
0243     clearProperties();
0244 }
0245 
0246 void KisBrushHud::slotCanvasResourceChanged(int key, const QVariant &resource)
0247 {
0248     Q_UNUSED(resource);
0249 
0250     if (key == KoCanvasResource::CurrentPaintOpPreset) {
0251         updateProperties();
0252     }
0253 }
0254 
0255 void KisBrushHud::paintEvent(QPaintEvent *event)
0256 {
0257     QColor bgColor = palette().color(QPalette::Window);
0258 
0259     QPainter painter(this);
0260     painter.fillRect(rect() & event->rect(), bgColor);
0261     painter.end();
0262 
0263     QWidget::paintEvent(event);
0264 }
0265 
0266 bool KisBrushHud::event(QEvent *event)
0267 {
0268     switch (event->type()) {
0269     case QEvent::TabletPress:
0270     case QEvent::TabletMove:
0271     case QEvent::TabletRelease:
0272         // Allow the tablet event to be translated to a mouse event on certain platforms
0273         break;
0274     case QEvent::MouseButtonPress:
0275     case QEvent::MouseMove:
0276     case QEvent::MouseButtonRelease:
0277     case QEvent::Wheel:
0278         event->accept();
0279         return true;
0280     default:
0281         break;
0282     }
0283 
0284     return QWidget::event(event);
0285 }
0286 
0287 bool KisBrushHud::eventFilter(QObject *watched, QEvent *event)
0288 {
0289     // LayoutRequest event is sent from a layout to its parent widget
0290     // when size requirements have been determined, i.e. sizeHint is available
0291     if (watched == m_d->wdgProperties && event->type() == QEvent::LayoutRequest)
0292     {
0293         int totalMargin = 2 * m_d->wdgPropertiesArea->frameWidth();
0294         m_d->wdgPropertiesArea->setMinimumWidth(m_d->wdgProperties->sizeHint().width() + totalMargin);
0295     }
0296     return false;
0297 }
0298 
0299 void KisBrushHud::slotConfigBrushHud()
0300 {
0301     if (!m_d->currentPreset) return;
0302 
0303     KisDlgConfigureBrushHud dlg(m_d->currentPreset);
0304     dlg.exec();
0305 
0306     slotReloadProperties();
0307 }
0308 
0309 void KisBrushHud::slotReloadPreset()
0310 {
0311     KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(m_d->provider->canvas());
0312     KIS_ASSERT_RECOVER_RETURN(canvas);
0313     canvas->viewManager()->actionCollection()->action("reload_preset_action")->trigger();
0314 }