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 }