Krita Source Code Documentation
Loading...
Searching...
No Matches
KisColorPatchesTableView.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2023 Sharaf Zaman <shzam@sdf.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#include <QCoreApplication>
10#include <QHeaderView>
11#include <QScrollBar>
12#include <QStandardItemModel>
13#include <QStyledItemDelegate>
14#include <QMouseEvent>
15
16#include <utility>
17
18#include "kis_color_patches.h"
19
20class KisColorPatchesTableDelegate : public QStyledItemDelegate
21{
22public:
24 : QStyledItemDelegate(parent)
25 {
26 }
27 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
28};
29
31 const QStyleOptionViewItem &option,
32 const QModelIndex &index) const
33{
34 const QColor color = index.data(Qt::UserRole).value<QColor>();
35
36 if (color.isValid()) {
37 // painter->fillRect(QRect(option.rect.topLeft(), size), color);
38 painter->fillRect(option.rect, color);
39 }
40}
41
59
60KisColorPatchesTableView::KisColorPatchesTableView(const QString &configPrefix, QWidget *parent)
61 : QTableView(parent)
62 , m_d(new KisColorPatchesTableView::Private(configPrefix))
63{
64 setShowGrid(false);
65 setSelectionBehavior(QAbstractItemView::SelectItems);
66 setSelectionMode(QAbstractItemView::NoSelection);
67 // makes sure that the patches don't go out of bounds, one can notice if kinetic scrolling enabled.
68 setStyleSheet("QTableView{ border: 0px}");
69 setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
70 setEditTriggers(QAbstractItemView::NoEditTriggers);
71
73
74 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(this);
75 if (scroller) {
76 QScrollerProperties props;
77 props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
78 QScrollerProperties::OvershootAlwaysOff);
79 props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
80 QScrollerProperties::OvershootAlwaysOff);
81 scroller->setScrollerProperties(props);
82 connect(scroller,
83 SIGNAL(stateChanged(QScroller::State)),
84 this,
85 SLOT(slotScrollerStateChanged(QScroller::State)));
86 }
88}
89
93
95{
96 m_d->colorPatches.clear();
97 m_d->colorPatches = colors;
98 redraw();
99}
100
102{
103 return m_d->colorPatches;
104}
105
107{
108 const KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector");
109
110 m_d->numCols = cfg.readEntry(m_d->configPrefix + "NumCols", 1);
111 m_d->numRows = cfg.readEntry(m_d->configPrefix + "NumRows", 1);
112
113 m_d->patchWidth = cfg.readEntry(m_d->configPrefix + "Width", 20);
114 m_d->patchHeight = cfg.readEntry(m_d->configPrefix + "Height", 20);
115 if (cfg.readEntry(m_d->configPrefix + "Alignment", false)) {
116 m_d->direction = KisColorPatches::Vertical;
117 } else {
118 m_d->direction = KisColorPatches::Horizontal;
119 }
120 m_d->patchCount = cfg.readEntry(m_d->configPrefix + "Count", 15);
121 const bool allowScrolling = cfg.readEntry(m_d->configPrefix + "Scrolling", true);
122 if (!allowScrolling) {
123 QScroller::scroller(this)->ungrabGesture(this);
124 horizontalScrollBar()->setEnabled(false);
125 verticalScrollBar()->setEnabled(false);
126 m_d->wasScrollingDisabled = true;
127 } else if (m_d->wasScrollingDisabled) {
128 QScroller *scroller = QScroller::scroller(this);
129 scroller->grabGesture(this, KisKineticScroller::getConfiguredGestureType());
130 horizontalScrollBar()->setEnabled(true);
131 verticalScrollBar()->setEnabled(true);
132 m_d->wasScrollingDisabled = false;
133 }
134
135 m_d->model.reset(new QStandardItemModel(m_d->numRows, m_d->numCols, this));
136
137 if (m_d->direction == KisColorPatches::Vertical) {
138 setMinimumWidth((m_d->patchWidth + 0) * m_d->numCols);
139 setMaximumWidth((m_d->patchWidth + 0) * m_d->numCols);
140
141 // reset if this was previously Horizontal
142 setMinimumHeight(m_d->patchHeight);
143 setMaximumHeight(QWIDGETSIZE_MAX);
144
145 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
146 } else {
147 setMinimumHeight((m_d->patchHeight + 0) * m_d->numRows);
148 setMaximumHeight((m_d->patchHeight + 0) * m_d->numRows);
149
150 // reset if this was previously Vertical
151 setMinimumWidth(m_d->patchWidth);
152 setMaximumWidth(QWIDGETSIZE_MAX);
153
154 setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
155 }
156
157 // minimum should always be set before default.
158 verticalHeader()->setMinimumSectionSize(m_d->patchHeight);
159 verticalHeader()->setMaximumSectionSize(m_d->patchHeight);
160 verticalHeader()->setDefaultSectionSize(m_d->patchHeight);
161 horizontalHeader()->setMinimumSectionSize(m_d->patchWidth);
162 horizontalHeader()->setMaximumSectionSize(m_d->patchWidth);
163 horizontalHeader()->setDefaultSectionSize(m_d->patchWidth);
164
165 // don't let the user resize us.
166 verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
167 horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
168
169 verticalHeader()->hide();
170 horizontalHeader()->hide();
171
172 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
173 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
174
175 setModel(m_d->model.data());
176 setItemDelegate(new KisColorPatchesTableDelegate(this));
177
178 redraw();
179}
180
182{
183 return QSize(m_d->patchWidth, m_d->patchHeight);
184}
185
187{
188 return m_d->patchCount;
189}
190
192{
193 m_d->colorPatches.removeAll(color);
194 m_d->colorPatches.prepend(color);
195
196 if (m_d->colorPatches.size() > 200) {
197 m_d->colorPatches.pop_back();
198 }
199
200 redraw();
201}
202
204{
205 if (m_d->direction == KisColorPatches::Horizontal) {
206 bool horizontal = qAbs(event->angleDelta().x()) > qAbs(event->angleDelta().y());
207 if (!horizontal) {
208 // HACK: here we make our normal vertical scroller, also do a horizontal scroll
209 QPoint modifiedAngleDelta = {event->angleDelta().y(), event->angleDelta().x()};
210 QWheelEvent modifiedEvent(event->position(),
211 event->globalPosition(),
212 event->pixelDelta(),
213 modifiedAngleDelta,
214 event->buttons(),
215 event->modifiers(),
216 event->phase(),
217 event->inverted());
218 horizontalScrollBar()->event(&modifiedEvent);
219 event->accept();
220 return;
221 }
222 }
223 QTableView::wheelEvent(event);
224}
225
227{
228 event->ignore();
229}
230
232{
233 event->ignore();
234}
235
237{
238 m_d->model->clear();
239
240 if (m_d->colorPatches.isEmpty()) {
241 return;
242 }
243
244 // a simple index, keeping count of how many patches have been put in the model.
245 int linearIndex = 0;
246 // sorry for the lack of term, think of pointer as pointing to an array and index iterating inside that
247 // array.
248 const int limit = ((m_d->direction == KisColorPatches::Vertical) ? m_d->numCols : m_d->numRows);
249 int pointer = 0;
250 // index should start from 1, because the first item is the "extra" button we have to take care of.
251 int index = 1 % limit;
252
253 Q_FOREACH (const KoColor &color, m_d->colorPatches) {
254 if (linearIndex > m_d->patchCount) {
255 break;
256 }
257 // We handle two cases:
258 // (1) say if numColumn = 1 and we are on row = 0, then we should add a row than trying to draw in
259 // i + 1 column (since column never changes in this case).
260 // (2) we have reached the end of the "array". Create a new sequence.
261 if (index == 0) {
262 pointer++;
263 if (m_d->direction == KisColorPatches::Vertical) {
264 m_d->model->insertRow(pointer);
265 } else {
266 m_d->model->insertColumn(pointer);
267 }
268 }
269 QStandardItem *item = new QStandardItem;
270 item->setData(QVariant(), Qt::DisplayRole);
271 item->setData(color.toQColor(), Qt::UserRole);
272 if (m_d->direction == KisColorPatches::Vertical) {
273 m_d->model->setItem(pointer, index, item);
274 } else {
275 m_d->model->setItem(index, pointer, item);
276 }
277 index = (index + 1) % limit;
278 linearIndex++;
279 }
280}
281
282boost::optional<KoColor> KisColorPatchesTableView::colorPatchAt(const QPoint &pos) const
283{
284 // TODO(sh_zam): Is there a better way that doesn't have to involve mapFromGlobal?
285 const QModelIndex index = indexAt(mapFromGlobal(pos));
286 if (!index.isValid()) {
287 return boost::none;
288 }
289 int linearIndex = 0;
290 if (m_d->direction == KisColorPatches::Vertical) {
291 linearIndex = index.row() * m_d->model->columnCount() + index.column();
292 } else {
293 linearIndex = index.column() * m_d->model->rowCount() + index.row();
294 }
295 // since (0, 0) index has the additional button.
296 linearIndex -= 1;
297
298 if (linearIndex < 0 || linearIndex >= m_d->colorPatches.size()) {
299 return boost::none;
300 }
301 return m_d->colorPatches[linearIndex];
302}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void mouseReleaseEvent(QMouseEvent *event) override
void setColors(const QList< KoColor > &colors)
void slotScrollerStateChanged(QScroller::State state)
void wheelEvent(QWheelEvent *event) override
boost::optional< KoColor > colorPatchAt(const QPoint &pos) const
void addColorPatch(const KoColor &color)
QScopedPointer< Private > m_d
void mousePressEvent(QMouseEvent *event) override
KisColorPatchesTableView(const QString &configPrefix, QWidget *parent=nullptr)
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
KRITAWIDGETUTILS_EXPORT QScroller::ScrollerGestureType getConfiguredGestureType()
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)
QScopedPointer< QStandardItemModel > model