Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_predefined_brush_chooser.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
3 * SPDX-FileCopyrightText: 2009 Sven Langkamp <sven.langkamp@gmail.com>
4 * SPDX-FileCopyrightText: 2010 Cyrille Berger <cberger@cberger.net>
5 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
6 * SPDX-FileCopyrightText: 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
12
13#include <QtMath>
14#include <QLabel>
15#include <QLayout>
16#include <QCheckBox>
17#include <QVBoxLayout>
18#include <QHBoxLayout>
19#include <QGridLayout>
20#include <QPainter>
21#include <QAbstractItemDelegate>
22#include <klocalizedstring.h>
23
24#include <KoFileDialog.h>
25#include <KisKineticScroller.h>
26
27#include <KisResourceItemView.h>
29#include <KisResourceModel.h>
32
33#include <kis_icon.h>
35#include "kis_algebra_2d.h"
36#include "kis_painting_tweaks.h"
37#include "kis_slider_spin_box.h"
38#include "krita_utils.h"
40#include "kis_signals_blocker.h"
41
42#include "kis_imagepipe_brush.h"
45#include <kis_image_config.h>
46#include <KisMimeDatabase.h>
47
48#include "kis_global.h"
49#include "kis_gbr_brush.h"
50#include "kis_png_brush.h"
51#include "kis_debug.h"
52#include "kis_image.h"
56#include <KisStorageModel.h>
59
61
62#include <lager/state.hpp>
65
66using namespace KisBrushModel;
67using namespace KisWidgetConnectionUtils;
68
70class KisBrushDelegate : public QAbstractItemDelegate
71{
72public:
74 QObject * parent = 0)
75 : QAbstractItemDelegate(parent)
76 , m_metaDataModel(metaDataModel)
77 {}
78 ~KisBrushDelegate() override {}
80 void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
82 QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override {
83 return option.decorationSize;
84 }
85
86private:
88};
89
90void KisBrushDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
91{
92 if (! index.isValid())
93 return;
94
95 QImage thumbnail = index.data(Qt::UserRole + KisAbstractResourceModel::Thumbnail).value<QImage>();
96
97 const QRect itemRect = kisGrowRect(option.rect, -1);
98 const qreal devicePixelRatioF = painter->device()->devicePixelRatioF();
99
100 const QSize hidpiSize = itemRect.size() * devicePixelRatioF;
102 hidpiSize,
103 Qt::KeepAspectRatio,
104 Qt::SmoothTransformation);
105 thumbnail.setDevicePixelRatio(devicePixelRatioF);
106
107 painter->save();
108
109 const int resourceId = index.data(Qt::UserRole + KisAbstractResourceModel::Id).toInt();
110 const bool hasImageType = m_metaDataModel->metaDataValue(resourceId, KisBrush::brushTypeMetaDataKey).toBool();
111
112 if (hasImageType) {
114
115 const int baseSize = qCeil(itemRect.width() / 5.0);
116 QImage brush(2 * baseSize, 2 * baseSize, QImage::Format_ARGB32);
117 brush.fill(Qt::white);
118 QPainter gc(&brush);
119
120 gc.setPen(Qt::NoPen);
121 gc.setBrush(QColor(200,200,200));
122 gc.drawRect(QRect(0,0,baseSize,baseSize));
123 gc.drawRect(QRect(baseSize,baseSize,baseSize,baseSize));
124
125 painter->setBrush(brush);
126
127 painter->setBrushOrigin(itemRect.topLeft());
128 painter->drawRect(itemRect);
129 painter->setBrush(Qt::NoBrush);
130
131 } else {
133 painter->setBrush(Qt::white);
134 painter->setPen(Qt::NoPen);
135 painter->drawRect(itemRect);
136 }
137
138 int dx = (itemRect.width() * devicePixelRatioF - thumbnail.width()) / 2 / devicePixelRatioF;
139 int dy = (itemRect.height() * devicePixelRatioF - thumbnail.height()) / 2 / devicePixelRatioF;
140 painter->drawImage(itemRect.x() + dx, itemRect.y() + dy, thumbnail);
141
142 if (option.state & QStyle::State_Selected) {
143 painter->setClipRect(option.rect);
144 painter->setPen(QPen(option.palette.highlight(), 2.0));
145 KritaUtils::renderExactRect(painter, itemRect);
146 painter->setCompositionMode(QPainter::CompositionMode_HardLight);
147 painter->setOpacity(0.65);
148 painter->fillRect(itemRect, option.palette.highlight());
149 }
150
151 painter->restore();
152}
153
163
166 QWidget *parent, const char *name)
167 : QWidget(parent),
168 m_d(new Private(model)),
169 m_stampBrushWidget(0),
170 m_clipboardBrushWidget(0)
171{
172 setObjectName(name);
173
174 setupUi(this);
175
176 brushSizeSpinBox->setRange(0, maxBrushSize, 2);
177 brushSizeSpinBox->setValue(5);
178 brushSizeSpinBox->setExponentRatio(3.0);
179 brushSizeSpinBox->setSuffix(i18n(" px"));
180 brushSizeSpinBox->setExponentRatio(3.0);
181
182 connect(m_d->model, &KisPredefinedBrushModel::brushNameChanged,
183 brushTipNameLabel, &KSqueezedTextLabel::setText);
184 m_d->model->LAGER_QT(brushName).nudge();
185
186 connect(m_d->model, &KisPredefinedBrushModel::brushDetailsChanged,
187 brushDetailsLabel, &QLabel::setText);
188 m_d->model->LAGER_QT(brushDetails).nudge();
189
190 connectControl(brushSizeSpinBox, m_d->model, "brushSize");
191
192 brushRotationAngleSelector->setDecimals(0);
193
194 connectControl(brushRotationAngleSelector, m_d->model, "angle");
195
196 brushSpacingSelectionWidget->setSpacing(true, 1.0);
197
198 connectControl(brushSpacingSelectionWidget, m_d->model, "aggregatedSpacing");
199
201 m_itemChooser->setObjectName("brush_selector");
202
208 m_itemChooser->setMinimumWidth(100);
209 m_itemChooser->setMinimumHeight(150);
210 m_itemChooser->showImportExportBtns(false); // turn the import and delete buttons since we want control over them
211
212 presetsLayout->addWidget(m_itemChooser);
213
216 connect(m_d->model, &KisPredefinedBrushModel::resourceSignatureChanged,
218
219 slotBrushPropertyChanged(m_d->model->resourceSignature());
220
221
222 addPresetButton->setIcon(KisIconUtils::loadIcon("list-add"));
223 deleteBrushTipButton->setIcon(KisIconUtils::loadIcon("edit-delete"));
224
225 connect(addPresetButton, SIGNAL(clicked(bool)), this, SLOT(slotImportNewBrushResource()));
226 connect(deleteBrushTipButton, SIGNAL(clicked(bool)), this, SLOT(slotDeleteBrushResource()));
227
228 stampButton->setIcon(KisIconUtils::loadIcon("list-add"));
229 stampButton->setToolTip(i18n("Creates a brush tip from the current image selection."
230 "\n If no selection is present the whole image will be used."));
231
232 clipboardButton->setIcon(KisIconUtils::loadIcon("list-add"));
233 clipboardButton->setToolTip(i18n("Creates a brush tip from the image in the clipboard."));
234
235 connect(stampButton, SIGNAL(clicked()), this, SLOT(slotOpenStampBrush()));
236 connect(clipboardButton, SIGNAL(clicked()), SLOT(slotOpenClipboardBrush()));
237
238 resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0"));
239 connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush()));
240
241 intAdjustmentMidPoint->setRange(0, 255);
242 intAdjustmentMidPoint->setPageStep(10);
243 intAdjustmentMidPoint->setSingleStep(1);
244 intAdjustmentMidPoint->setPrefix(i18nc("@label:slider", "Neutral point: "));
245 connectControl(intAdjustmentMidPoint, m_d->model, "adjustmentMidPoint");
246 connectControl(chkAutoMidPoint, m_d->model, "autoAdjustMidPoint");
247
248 intBrightnessAdjustment->setRange(-100, 100);
249 intBrightnessAdjustment->setPageStep(10);
250 intBrightnessAdjustment->setSingleStep(1);
251 intBrightnessAdjustment->setSuffix("%");
252 intBrightnessAdjustment->setPrefix(i18nc("@label:slider", "Brightness: "));
253 connectControl(intBrightnessAdjustment, m_d->model, "brightnessAdjustment");
254
255 intContrastAdjustment->setRange(-100, 100);
256 intContrastAdjustment->setPageStep(10);
257 intContrastAdjustment->setSingleStep(1);
258 intContrastAdjustment->setSuffix("%");
259 intContrastAdjustment->setPrefix(i18nc("@label:slider", "Contrast: "));
260 connectControl(intContrastAdjustment, m_d->model, "contrastAdjustment");
261
262 btnResetAdjustments->setToolTip(i18nc("@info:tooltip", "Resets all the adjustments to default values:\n Neutral Point: 127\n Brightness: 0%\n Contrast: 0%"));
263 connect(btnResetAdjustments, SIGNAL(clicked()), SLOT(slotResetAdjustments()));
264
265 connectControlState(cmbBrushMode, m_d->model, "applicationSwitchState", "application");
266
267 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
268 intAdjustmentMidPoint, &KisSliderSpinBox::setEnabled);
269 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
270 intBrightnessAdjustment, &KisSliderSpinBox::setEnabled);
271 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
272 intContrastAdjustment, &KisSliderSpinBox::setEnabled);
273 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
274 chkAutoMidPoint, &KisSliderSpinBox::setEnabled);
275 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
276 btnResetAdjustments, &KisSliderSpinBox::setEnabled);
277
278 m_d->model->LAGER_QT(adjustmentsEnabled).nudge();
279}
280
284
286{
287 KisBrushSP brush = m_itemChooser->currentResource().dynamicCast<KisBrush>();
288 if (brush) {
289 KisBrushModel::CommonData commonData;
291
292 KisPredefinedBrushFactory::loadFromBrushResource(commonData, predefinedData, brush);
293
294 if (m_d->model->applicationSwitchState().items.size() >= LIGHTNESSMAP) {
295 predefinedData.application = LIGHTNESSMAP;
296 }
297
298 m_d->model->m_commonData.set(commonData);
299 m_d->model->m_predefinedBrushData.set(predefinedData);
300 m_d->model->m_commonBrushSizeData.set(KisPredefinedBrushModel::effectiveBrushSize(predefinedData));
301 }
302}
303
305{
306 if(!m_stampBrushWidget) {
307 m_stampBrushWidget = new KisCustomBrushWidget(this, i18n("Stamp"), m_image);
308 m_stampBrushWidget->setModal(false);
309 connect(m_stampBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )),
311 } else {
313 }
314
315 QDialog::DialogCode result = (QDialog::DialogCode)m_stampBrushWidget->exec();
316
317 if(result) {
318 // noop
319 }
320}
322{
324 m_clipboardBrushWidget = new KisClipboardBrushWidget(this, i18n("Clipboard"), m_image);
325 m_clipboardBrushWidget->setModal(true);
326 connect(m_clipboardBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP )),
328 }
329
330 QDialog::DialogCode result = (QDialog::DialogCode)m_clipboardBrushWidget->exec();
331
332 if(result) {
333 // noop
334 }
335}
336
338{
340
341 KisBrushModel::PredefinedBrushData predefinedBrushData = m_d->model->bakedOptionData();
342 if (resource->signature() == predefinedBrushData.resourceSignature) return;
343
344 KisBrushModel::CommonData commonBrushData;
345 KisPredefinedBrushFactory::loadFromBrushResource(commonBrushData, predefinedBrushData, resource.dynamicCast<KisBrush>());
346
347 predefinedBrushData.scale = 1.0;
348 predefinedBrushData.application = KisPredefinedBrushModel::effectiveBrushApplication(predefinedBrushData, m_d->model->m_supportsHSLBrushTips.get());
349
350 // TODO: check what happens when we add a new brush
351 if (this->preserveBrushPresetSettings->isChecked()) {
352 commonBrushData.spacing = m_d->model->spacing();
353 commonBrushData.useAutoSpacing = m_d->model->useAutoSpacing();
354 commonBrushData.autoSpacingCoeff = m_d->model->autoSpacingCoeff();
355 } else {
356 m_d->model->m_commonBrushSizeData.set(predefinedBrushData.baseSize.width());
357 }
358
359 m_d->model->m_commonData.set(commonBrushData);
360 m_d->model->m_predefinedBrushData.set(predefinedBrushData);
361}
362
368
370{
371 m_d->model->m_predefinedBrushData.update(
374
375 brush.adjustmentMidPoint = defaultBrush.adjustmentMidPoint;
376 brush.brightnessAdjustment = defaultBrush.brightnessAdjustment;
377 brush.contrastAdjustment = defaultBrush.contrastAdjustment;
378 brush.autoAdjustMidPoint = defaultBrush.autoAdjustMidPoint;
379
380 return brush;
381 });
382}
383
388
390{
391 m_image = image;
392}
393
395{
396 return m_d->model->LAGER_QT(lightnessModeEnabled);
397}
398
400 // reflects m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Import)
401 // but adds the .abr files support, as it was in Krita 4
403 QString abrMimeType = "image/x-adobe-brushlibrary";
404 mimeTypes.append(abrMimeType);
405 KoFileDialog dialog(0, KoFileDialog::OpenFiles, "OpenDocument");
406 dialog.setMimeTypeFilters(mimeTypes);
407 dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
408 Q_FOREACH(const QString &filename, dialog.filenames()) {
409 if (QFileInfo(filename).exists() && QFileInfo(filename).isReadable()) {
410 if (KisMimeDatabase::mimeTypeForFile(filename).contains(abrMimeType)) {
412 } else {
414 }
415 }
416 }
417 m_itemChooser->tagFilterModel()->sort(Qt::DisplayRole);
418}
419
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
The resource item delegate for rendering the resource preview.
KisBrushDelegate(KisResourceMetaDataModel *metaDataModel, QObject *parent=0)
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const override
reimplemented
KisResourceMetaDataModel * m_metaDataModel
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override
reimplemented
static const QString brushTypeMetaDataKey
Definition kis_brush.h:343
void setImage(KisImageWSP image)
static KisResourcesInterfaceSP instance()
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
lager::reader< bool > lightnessModeEnabled() const
KisResourceItemChooser * m_itemChooser
void slotBrushPropertyChanged(KoResourceSignature signature)
void slotBrushSelected(KoResourceSP resource)
KisCustomBrushWidget * m_stampBrushWidget
KisPredefinedBrushChooser(int maxBrushSize, KisPredefinedBrushModel *model, QWidget *parent=0, const char *name=0)
const QScopedPointer< Private > m_d
KisClipboardBrushWidget * m_clipboardBrushWidget
static void loadFromBrushResource(KisBrushModel::CommonData &commonData, KisBrushModel::PredefinedBrushData &predefinedBrushData, KisBrushSP brushResource)
static qreal effectiveBrushSize(PredefinedBrushData predefinedData)
static enumBrushApplication effectiveBrushApplication(PredefinedBrushData predefinedData, bool supportsHSLBrushTips)
void setCurrentResource(KoResourceSP resource)
Sets the item representing the resource as selected.
void resourceSelected(KoResourceSP resource)
Emitted when a resource was selected.
void setRowHeight(int rowHeight)
Sets the height of the view rows.
KisTagFilterResourceProxyModel * tagFilterModel() const
void setItemDelegate(QAbstractItemDelegate *delegate)
Sets a custom delegate for the view.
static KisResourceLoaderRegistry * instance()
QStringList mimeTypes(const QString &resourceType) const
QVariant metaDataValue(int resourceId, const QString &key)
static KisResourceMetaDataModel * resourceMetadataModel()
static KisResourceThumbnailCache * instance()
QImage getImage(const QModelIndex &index, const QSize size=QSize(-1, -1), Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode=Qt::FastTransformation)
static KoResourceSP importResourceFileWithUserInput(QWidget *widgetParent, QString storageLocation, QString resourceType, QString resourceFilepath)
static KisStorageModel * instance()
bool importStorage(QString filename, StorageImportOption importOption) const
A simple wrapper object for the main information about the resource.
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
@ LIGHTNESSMAP
Definition kis_brush.h:41
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
QIcon loadIcon(const QString &name)
void connectControl(KisCompositeOpListWidget *widget, QObject *source, const char *property)
void connectControlState(QSpinBox *spinBox, QObject *source, const char *readStateProperty, const char *writeProperty)
void renderExactRect(QPainter *p, const QRect &rc)
const QString Brushes
KoResourceSignature resourceSignature