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"
44#include <kis_image_config.h>
45#include <KisMimeDatabase.h>
46
47#include "kis_global.h"
48#include "kis_gbr_brush.h"
49#include "kis_png_brush.h"
50#include "kis_debug.h"
51#include "kis_image.h"
55#include <KisStorageModel.h>
58
60
61#include <lager/state.hpp>
64
65using namespace KisBrushModel;
66using namespace KisWidgetConnectionUtils;
67
69class KisBrushDelegate : public QAbstractItemDelegate
70{
71public:
73 QObject * parent = 0)
74 : QAbstractItemDelegate(parent)
75 , m_metaDataModel(metaDataModel)
76 {}
77 ~KisBrushDelegate() override {}
79 void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
81 QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &) const override {
82 return option.decorationSize;
83 }
84
85private:
87};
88
89void KisBrushDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
90{
91 if (! index.isValid())
92 return;
93
94 QImage thumbnail = index.data(Qt::UserRole + KisAbstractResourceModel::Thumbnail).value<QImage>();
95
96 const QRect itemRect = kisGrowRect(option.rect, -1);
97 const qreal devicePixelRatioF = painter->device()->devicePixelRatioF();
98
99 const QSize hidpiSize = itemRect.size() * devicePixelRatioF;
101 hidpiSize,
102 Qt::KeepAspectRatio,
103 Qt::SmoothTransformation);
104 thumbnail.setDevicePixelRatio(devicePixelRatioF);
105
106 painter->save();
107
108 const int resourceId = index.data(Qt::UserRole + KisAbstractResourceModel::Id).toInt();
109 const bool hasImageType = m_metaDataModel->metaDataValue(resourceId, KisBrush::brushTypeMetaDataKey).toBool();
110
111 if (hasImageType) {
113
114 const int baseSize = qCeil(itemRect.width() / 5.0);
115 QImage brush(2 * baseSize, 2 * baseSize, QImage::Format_ARGB32);
116 brush.fill(Qt::white);
117 QPainter gc(&brush);
118
119 gc.setPen(Qt::NoPen);
120 gc.setBrush(QColor(200,200,200));
121 gc.drawRect(QRect(0,0,baseSize,baseSize));
122 gc.drawRect(QRect(baseSize,baseSize,baseSize,baseSize));
123
124 painter->setBrush(brush);
125
126 painter->setBrushOrigin(itemRect.topLeft());
127 painter->drawRect(itemRect);
128 painter->setBrush(Qt::NoBrush);
129
130 } else {
132 painter->setBrush(Qt::white);
133 painter->setPen(Qt::NoPen);
134 painter->drawRect(itemRect);
135 }
136
137 int dx = (itemRect.width() * devicePixelRatioF - thumbnail.width()) / 2 / devicePixelRatioF;
138 int dy = (itemRect.height() * devicePixelRatioF - thumbnail.height()) / 2 / devicePixelRatioF;
139 painter->drawImage(itemRect.x() + dx, itemRect.y() + dy, thumbnail);
140
141 if (option.state & QStyle::State_Selected) {
142 painter->setClipRect(option.rect);
143 painter->setPen(QPen(option.palette.highlight(), 2.0));
144 KritaUtils::renderExactRect(painter, itemRect);
145 painter->setCompositionMode(QPainter::CompositionMode_HardLight);
146 painter->setOpacity(0.65);
147 painter->fillRect(itemRect, option.palette.highlight());
148 }
149
150 painter->restore();
151}
152
162
165 QWidget *parent, const char *name)
166 : QWidget(parent),
167 m_d(new Private(model))
168{
169 setObjectName(name);
170
171 setupUi(this);
172
173 brushSizeSpinBox->setRange(0, maxBrushSize, 2);
174 brushSizeSpinBox->setValue(5);
175 brushSizeSpinBox->setExponentRatio(3.0);
176 brushSizeSpinBox->setSuffix(i18n(" px"));
177 brushSizeSpinBox->setExponentRatio(3.0);
178
179 connect(m_d->model, &KisPredefinedBrushModel::brushNameChanged,
180 brushTipNameLabel, &KSqueezedTextLabel::setText);
181 m_d->model->LAGER_QT(brushName).nudge();
182
183 connect(m_d->model, &KisPredefinedBrushModel::brushDetailsChanged,
184 brushDetailsLabel, &QLabel::setText);
185 m_d->model->LAGER_QT(brushDetails).nudge();
186
187 connectControl(brushSizeSpinBox, m_d->model, "brushSize");
188
189 brushRotationAngleSelector->setDecimals(0);
190
191 connectControl(brushRotationAngleSelector, m_d->model, "angle");
192
193 brushSpacingSelectionWidget->setSpacing(true, 1.0);
194
195 connectControl(brushSpacingSelectionWidget, m_d->model, "aggregatedSpacing");
196
198 m_itemChooser->setObjectName("brush_selector");
199
205 m_itemChooser->setMinimumWidth(100);
206 m_itemChooser->setMinimumHeight(150);
207 m_itemChooser->showImportExportBtns(false); // turn the import and delete buttons since we want control over them
208
209 presetsLayout->addWidget(m_itemChooser);
210
213 connect(m_d->model, &KisPredefinedBrushModel::resourceSignatureChanged,
215
216 slotBrushPropertyChanged(m_d->model->resourceSignature());
217
218
219 addPresetButton->setIcon(KisIconUtils::loadIcon("list-add"));
220 deleteBrushTipButton->setIcon(KisIconUtils::loadIcon("edit-delete"));
221
222 connect(addPresetButton, SIGNAL(clicked(bool)), this, SLOT(slotImportNewBrushResource()));
223 connect(deleteBrushTipButton, SIGNAL(clicked(bool)), this, SLOT(slotDeleteBrushResource()));
224
225 customButton->setIcon(KisIconUtils::loadIcon("list-add"));
226 customButton->setToolTip(i18n("Creates a brush tip from the current image, selection, or clipboard."));
227
228 connect(customButton, SIGNAL(clicked()), this, SLOT(slotOpenCustomBrush()));
229
230 resetBrushButton->setToolTip(i18n("Reloads Spacing from file\nSets Scale to 1.0\nSets Rotation to 0.0"));
231 connect(resetBrushButton, SIGNAL(clicked()), SLOT(slotResetBrush()));
232
233 intAdjustmentMidPoint->setRange(0, 255);
234 intAdjustmentMidPoint->setPageStep(10);
235 intAdjustmentMidPoint->setSingleStep(1);
236 intAdjustmentMidPoint->setPrefix(i18nc("@label:slider", "Neutral point: "));
237 connectControl(intAdjustmentMidPoint, m_d->model, "adjustmentMidPoint");
238 connectControl(chkAutoMidPoint, m_d->model, "autoAdjustMidPoint");
239
240 intBrightnessAdjustment->setRange(-100, 100);
241 intBrightnessAdjustment->setPageStep(10);
242 intBrightnessAdjustment->setSingleStep(1);
243 intBrightnessAdjustment->setSuffix("%");
244 intBrightnessAdjustment->setPrefix(i18nc("@label:slider", "Brightness: "));
245 connectControl(intBrightnessAdjustment, m_d->model, "brightnessAdjustment");
246
247 intContrastAdjustment->setRange(-100, 100);
248 intContrastAdjustment->setPageStep(10);
249 intContrastAdjustment->setSingleStep(1);
250 intContrastAdjustment->setSuffix("%");
251 intContrastAdjustment->setPrefix(i18nc("@label:slider", "Contrast: "));
252 connectControl(intContrastAdjustment, m_d->model, "contrastAdjustment");
253
254 btnResetAdjustments->setToolTip(i18nc("@info:tooltip", "Resets all the adjustments to default values:\n Neutral Point: 127\n Brightness: 0%\n Contrast: 0%"));
255 connect(btnResetAdjustments, SIGNAL(clicked()), SLOT(slotResetAdjustments()));
256
257 connectControlState(cmbBrushMode, m_d->model, "applicationSwitchState", "application");
258
259 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
260 intAdjustmentMidPoint, &KisSliderSpinBox::setEnabled);
261 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
262 intBrightnessAdjustment, &KisSliderSpinBox::setEnabled);
263 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
264 intContrastAdjustment, &KisSliderSpinBox::setEnabled);
265 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
266 chkAutoMidPoint, &KisSliderSpinBox::setEnabled);
267 connect(m_d->model, &KisPredefinedBrushModel::adjustmentsEnabledChanged,
268 btnResetAdjustments, &KisSliderSpinBox::setEnabled);
269
270 m_d->model->LAGER_QT(adjustmentsEnabled).nudge();
271}
272
276
278{
279 KisBrushSP brush = m_itemChooser->currentResource().dynamicCast<KisBrush>();
280 if (brush) {
281 KisBrushModel::CommonData commonData;
283
284 KisPredefinedBrushFactory::loadFromBrushResource(commonData, predefinedData, brush);
285
286 if (m_d->model->applicationSwitchState().items.size() >= LIGHTNESSMAP) {
287 predefinedData.application = LIGHTNESSMAP;
288 }
289
290 m_d->model->m_commonData.set(commonData);
291 m_d->model->m_predefinedBrushData.set(predefinedData);
292 m_d->model->m_commonBrushSizeData.set(KisPredefinedBrushModel::effectiveBrushSize(predefinedData));
293 }
294}
295
297{
298 KisCustomBrushWidget* customBrushWidget = new KisCustomBrushWidget(this, i18n("Create New Brushtip"));
299 connect(customBrushWidget, SIGNAL(sigNewPredefinedBrush(KoResourceSP)),
301 customBrushWidget->show();
302}
303
305{
307
308 KisBrushModel::PredefinedBrushData predefinedBrushData = m_d->model->bakedOptionData();
309 if (resource->signature() == predefinedBrushData.resourceSignature) return;
310
311 KisBrushModel::CommonData commonBrushData;
312 KisPredefinedBrushFactory::loadFromBrushResource(commonBrushData, predefinedBrushData, resource.dynamicCast<KisBrush>());
313
314 predefinedBrushData.scale = 1.0;
315 predefinedBrushData.application = KisPredefinedBrushModel::effectiveBrushApplication(predefinedBrushData, m_d->model->m_supportsHSLBrushTips.get());
316
317 // TODO: check what happens when we add a new brush
318 if (this->preserveBrushPresetSettings->isChecked()) {
319 commonBrushData.spacing = m_d->model->spacing();
320 commonBrushData.useAutoSpacing = m_d->model->useAutoSpacing();
321 commonBrushData.autoSpacingCoeff = m_d->model->autoSpacingCoeff();
322 } else {
323 m_d->model->m_commonBrushSizeData.set(predefinedBrushData.baseSize.width());
324 }
325
326 m_d->model->m_commonData.set(commonBrushData);
327 m_d->model->m_predefinedBrushData.set(predefinedBrushData);
328}
329
335
337{
338 m_d->model->m_predefinedBrushData.update(
341
342 brush.adjustmentMidPoint = defaultBrush.adjustmentMidPoint;
343 brush.brightnessAdjustment = defaultBrush.brightnessAdjustment;
344 brush.contrastAdjustment = defaultBrush.contrastAdjustment;
345 brush.autoAdjustMidPoint = defaultBrush.autoAdjustMidPoint;
346
347 return brush;
348 });
349}
350
355
357{
358 return m_d->model->LAGER_QT(lightnessModeEnabled);
359}
360
362 // reflects m_itemChooser->slotButtonClicked(KisResourceItemChooser::Button_Import)
363 // but adds the .abr files support, as it was in Krita 4
365 QString abrMimeType = "image/x-adobe-brushlibrary";
366 mimeTypes.append(abrMimeType);
367 KoFileDialog dialog(0, KoFileDialog::OpenFiles, "OpenDocument");
368 dialog.setMimeTypeFilters(mimeTypes);
369 dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
370 Q_FOREACH(const QString &filename, dialog.filenames()) {
371 if (QFileInfo(filename).exists() && QFileInfo(filename).isReadable()) {
372 if (KisMimeDatabase::mimeTypeForFile(filename).contains(abrMimeType)) {
374 } else {
376 }
377 }
378 }
379 m_itemChooser->tagFilterModel()->sort(Qt::DisplayRole);
380}
381
KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
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
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)
KisPredefinedBrushChooser(int maxBrushSize, KisPredefinedBrushModel *model, QWidget *parent=0, const char *name=0)
const QScopedPointer< Private > m_d
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(const 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