1 /*
2 * Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "kis_brush_hud.h"
20
21 #include <QGuiApplication>
22 #include <QVBoxLayout>
23 #include <QHBoxLayout>
24 #include <QPointer>
25 #include <QLabel>
26 #include <QPainter>
27 #include <QPaintEvent>
28 #include <QScrollArea>
29 #include <QEvent>
30 #include <QToolButton>
31 #include <QAction>
32
33 #include "kis_uniform_paintop_property.h"
34 #include "kis_slider_based_paintop_property.h"
35 #include "kis_uniform_paintop_property_widget.h"
36 #include "kis_canvas_resource_provider.h"
37 #include "kis_paintop_preset.h"
38 #include "kis_paintop_settings.h"
39 #include "kis_signal_auto_connection.h"
40 #include "kis_paintop_settings_update_proxy.h"
41 #include "kis_icon_utils.h"
42 #include "kis_dlg_brush_hud_config.h"
43 #include "kis_brush_hud_properties_config.h"
44 #include "kis_elided_label.h"
45
46 #include "kis_canvas2.h"
47 #include "KisViewManager.h"
48 #include "kactioncollection.h"
49
50 #include "kis_debug.h"
51
52
53 struct KisBrushHud::Private
54 {
55 QPointer<KisElidedLabel> lblPresetName;
56 QPointer<QLabel> lblPresetIcon;
57 QPointer<QWidget> wdgProperties;
58 QPointer<QScrollArea> wdgPropertiesArea;
59 QPointer<QVBoxLayout> propertiesLayout;
60 QPointer<QToolButton> btnReloadPreset;
61 QPointer<QToolButton> btnConfigure;
62
63 KisCanvasResourceProvider *provider;
64
65 KisSignalAutoConnectionsStore connections;
66 KisSignalAutoConnectionsStore presetConnections;
67
68 KisPaintOpPresetSP currentPreset;
69 };
70
KisBrushHud(KisCanvasResourceProvider * provider,QWidget * parent)71 KisBrushHud::KisBrushHud(KisCanvasResourceProvider *provider, QWidget *parent)
72 : QWidget(parent, Qt::FramelessWindowHint),
73 m_d(new Private)
74 {
75 m_d->provider = provider;
76
77 QVBoxLayout *layout = new QVBoxLayout();
78
79 QHBoxLayout *labelLayout = new QHBoxLayout();
80 m_d->lblPresetIcon = new QLabel(this);
81 const QSize iconSize = QSize(22,22);
82 m_d->lblPresetIcon->setMinimumSize(iconSize);
83 m_d->lblPresetIcon->setMaximumSize(iconSize);
84 m_d->lblPresetIcon->setScaledContents(true);
85
86 m_d->lblPresetName = new KisElidedLabel("<Preset Name>", Qt::ElideMiddle, this);
87
88 m_d->btnReloadPreset = new QToolButton(this);
89 m_d->btnReloadPreset->setAutoRaise(true);
90
91 m_d->btnConfigure = new QToolButton(this);
92 m_d->btnConfigure->setAutoRaise(true);
93
94 connect(m_d->btnReloadPreset, SIGNAL(clicked()), SLOT(slotReloadPreset()));
95 connect(m_d->btnConfigure, SIGNAL(clicked()), SLOT(slotConfigBrushHud()));
96
97 labelLayout->addWidget(m_d->lblPresetIcon);
98 labelLayout->addWidget(m_d->lblPresetName);
99 labelLayout->addWidget(m_d->btnReloadPreset);
100 labelLayout->addWidget(m_d->btnConfigure);
101
102 layout->addLayout(labelLayout);
103
104 m_d->wdgPropertiesArea = new QScrollArea(this);
105 m_d->wdgPropertiesArea->setAlignment(Qt::AlignLeft | Qt::AlignTop);
106 m_d->wdgPropertiesArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
107
108 m_d->wdgPropertiesArea->setWidgetResizable(true);
109
110 m_d->wdgProperties = new QWidget(this);
111 m_d->propertiesLayout = new QVBoxLayout(this);
112 m_d->propertiesLayout->setSpacing(0);
113 m_d->propertiesLayout->setContentsMargins(0, 0, 22, 0);
114 m_d->propertiesLayout->setSizeConstraint(QLayout::SetMinimumSize);
115
116 // not adding any widgets until explicitly requested
117
118 m_d->wdgProperties->setLayout(m_d->propertiesLayout);
119 m_d->wdgPropertiesArea->setWidget(m_d->wdgProperties);
120 layout->addWidget(m_d->wdgPropertiesArea);
121
122 // unfortunately the sizeHint() function of QScrollArea is pretty broken
123 // and it would add another event loop iteration to react to it anyway,
124 // so let's just catch LayoutRequest events from the properties widget directly
125 m_d->wdgProperties->installEventFilter(this);
126
127 updateIcons();
128
129 setLayout(layout);
130 setCursor(Qt::ArrowCursor);
131
132 // Prevent tablet events from being captured by the canvas
133 setAttribute(Qt::WA_NoMousePropagation, true);
134 }
135
136
137
~KisBrushHud()138 KisBrushHud::~KisBrushHud()
139 {
140 }
141
sizeHint() const142 QSize KisBrushHud::sizeHint() const
143 {
144 QSize size = QWidget::sizeHint();
145 return QSize(size.width(), parentWidget()->height());
146 }
147
updateIcons()148 void KisBrushHud::updateIcons()
149 {
150 this->setPalette(qApp->palette());
151 for(int i=0; i<this->children().size(); i++) {
152 QWidget *w = qobject_cast<QWidget*>(this->children().at(i));
153 if (w) {
154 w->setPalette(qApp->palette());
155 }
156 }
157 for(int i=0; i<m_d->wdgProperties->children().size(); i++) {
158 KisUniformPaintOpPropertyWidget *w = qobject_cast<KisUniformPaintOpPropertyWidget*>(m_d->wdgProperties->children().at(i));
159 if (w) {
160 w->slotThemeChanged(qApp->palette());
161 }
162 }
163 m_d->btnReloadPreset->setIcon(KisIconUtils::loadIcon("view-refresh"));
164 m_d->btnConfigure->setIcon(KisIconUtils::loadIcon("applications-system"));
165 }
166
slotReloadProperties()167 void KisBrushHud::slotReloadProperties()
168 {
169 m_d->presetConnections.clear();
170 clearProperties();
171 updateProperties();
172 }
173
clearProperties() const174 void KisBrushHud::clearProperties() const
175 {
176 while (m_d->propertiesLayout->count()) {
177 QLayoutItem *item = m_d->propertiesLayout->takeAt(0);
178
179 QWidget *w = item->widget();
180 if (w) {
181 w->deleteLater();
182 }
183
184 delete item;
185 }
186
187 m_d->currentPreset.clear();
188 }
189
updateProperties()190 void KisBrushHud::updateProperties()
191 {
192 KisPaintOpPresetSP preset = m_d->provider->currentPreset();
193
194 if (preset == m_d->currentPreset) return;
195
196 m_d->presetConnections.clear();
197 clearProperties();
198
199 m_d->currentPreset = preset;
200 m_d->presetConnections.addConnection(
201 m_d->currentPreset->updateProxy(), SIGNAL(sigUniformPropertiesChanged()),
202 this, SLOT(slotReloadProperties()));
203
204 m_d->lblPresetIcon->setPixmap(QPixmap::fromImage(preset->image()));
205 m_d->lblPresetName->setLongText(preset->name());
206
207 QList<KisUniformPaintOpPropertySP> properties;
208
209 {
210 QList<KisUniformPaintOpPropertySP> allProperties = preset->uniformProperties();
211 QList<KisUniformPaintOpPropertySP> discardedProperties;
212
213 KisBrushHudPropertiesConfig cfg;
214 cfg.filterProperties(preset->paintOp().id(),
215 allProperties,
216 &properties,
217 &discardedProperties);
218 }
219
220 Q_FOREACH(auto property, properties) {
221 QWidget *w = 0;
222
223 if (!property->isVisible()) continue;
224
225 if (property->type() == KisUniformPaintOpProperty::Int) {
226 w = new KisUniformPaintOpPropertyIntSlider(property, m_d->wdgProperties);
227 } else if (property->type() == KisUniformPaintOpProperty::Double) {
228 w = new KisUniformPaintOpPropertyDoubleSlider(property, m_d->wdgProperties);
229 } else if (property->type() == KisUniformPaintOpProperty::Bool) {
230 w = new KisUniformPaintOpPropertyCheckBox(property, m_d->wdgProperties);
231 } else if (property->type() == KisUniformPaintOpProperty::Combo) {
232 w = new KisUniformPaintOpPropertyComboBox(property, m_d->wdgProperties);
233 }
234
235 if (w) {
236 w->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
237 m_d->propertiesLayout->addWidget(w);
238 }
239 }
240
241 m_d->propertiesLayout->addStretch();
242 }
243
showEvent(QShowEvent * event)244 void KisBrushHud::showEvent(QShowEvent *event)
245 {
246 m_d->connections.clear();
247 m_d->connections.addUniqueConnection(
248 m_d->provider->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)),
249 this, SLOT(slotCanvasResourceChanged(int,QVariant)));
250
251 updateProperties();
252
253 QWidget::showEvent(event);
254 }
255
hideEvent(QHideEvent * event)256 void KisBrushHud::hideEvent(QHideEvent *event)
257 {
258 m_d->connections.clear();
259 QWidget::hideEvent(event);
260
261 clearProperties();
262 }
263
slotCanvasResourceChanged(int key,const QVariant & resource)264 void KisBrushHud::slotCanvasResourceChanged(int key, const QVariant &resource)
265 {
266 Q_UNUSED(resource);
267
268 if (key == KisCanvasResourceProvider::CurrentPaintOpPreset) {
269 updateProperties();
270 }
271 }
272
paintEvent(QPaintEvent * event)273 void KisBrushHud::paintEvent(QPaintEvent *event)
274 {
275 QColor bgColor = palette().color(QPalette::Window);
276
277 QPainter painter(this);
278 painter.fillRect(rect() & event->rect(), bgColor);
279 painter.end();
280
281 QWidget::paintEvent(event);
282 }
283
event(QEvent * event)284 bool KisBrushHud::event(QEvent *event)
285 {
286 switch (event->type()) {
287 case QEvent::TabletPress:
288 case QEvent::TabletMove:
289 case QEvent::TabletRelease:
290 // Allow the tablet event to be translated to a mouse event on certain platforms
291 break;
292 case QEvent::MouseButtonPress:
293 case QEvent::MouseMove:
294 case QEvent::MouseButtonRelease:
295 case QEvent::Wheel:
296 event->accept();
297 return true;
298 case QEvent::LayoutRequest:
299 // resize when our layout determined a new recommended size
300 resize(sizeHint());
301 return true;
302 default:
303 break;
304 }
305
306 return QWidget::event(event);
307 }
308
eventFilter(QObject * watched,QEvent * event)309 bool KisBrushHud::eventFilter(QObject *watched, QEvent *event)
310 {
311 // LayoutRequest event is sent from a layout to its parent widget
312 // when size requirements have been determined, i.e. sizeHint is available
313 if (watched == m_d->wdgProperties && event->type() == QEvent::LayoutRequest)
314 {
315 int totalMargin = 2 * m_d->wdgPropertiesArea->frameWidth();
316 m_d->wdgPropertiesArea->setMinimumWidth(m_d->wdgProperties->sizeHint().width() + totalMargin);
317 }
318 return false;
319 }
320
slotConfigBrushHud()321 void KisBrushHud::slotConfigBrushHud()
322 {
323 if (!m_d->currentPreset) return;
324
325 KisDlgConfigureBrushHud dlg(m_d->currentPreset);
326 dlg.exec();
327
328 slotReloadProperties();
329 }
330
slotReloadPreset()331 void KisBrushHud::slotReloadPreset()
332 {
333 KisCanvas2* canvas = dynamic_cast<KisCanvas2*>(m_d->provider->canvas());
334 KIS_ASSERT_RECOVER_RETURN(canvas);
335 canvas->viewManager()->actionCollection()->action("reload_preset_action")->trigger();
336 }
337