1 #include "./paletteeditor.h"
2 #include "./colorbutton.h"
3
4 #include "ui_paletteeditor.h"
5
6 #include <QHeaderView>
7 #include <QLabel>
8 #include <QMetaProperty>
9 #include <QPainter>
10 #include <QStyle>
11 #include <QToolButton>
12
13 namespace QtUtilities {
14
15 enum { BrushRole = 33 };
16
PaletteEditor(QWidget * parent)17 PaletteEditor::PaletteEditor(QWidget *parent)
18 : QDialog(parent)
19 , m_ui(new Ui::PaletteEditor)
20 , m_currentColorGroup(QPalette::Active)
21 , m_paletteModel(new PaletteModel(this))
22 , m_modelUpdated(false)
23 , m_paletteUpdated(false)
24 , m_compute(true)
25 {
26 m_ui->setupUi(this);
27 m_ui->paletteView->setModel(m_paletteModel);
28 updatePreviewPalette();
29 updateStyledButton();
30 m_ui->paletteView->setModel(m_paletteModel);
31 auto *const delegate = new ColorDelegate(this);
32 m_ui->paletteView->setItemDelegate(delegate);
33 m_ui->paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers);
34 connect(m_paletteModel, &PaletteModel::paletteChanged, this, &PaletteEditor::paletteChanged);
35 m_ui->paletteView->setSelectionBehavior(QAbstractItemView::SelectRows);
36 m_ui->paletteView->setDragEnabled(true);
37 m_ui->paletteView->setDropIndicatorShown(true);
38 m_ui->paletteView->setRootIsDecorated(false);
39 m_ui->paletteView->setColumnHidden(2, true);
40 m_ui->paletteView->setColumnHidden(3, true);
41 }
42
~PaletteEditor()43 PaletteEditor::~PaletteEditor()
44 {
45 }
46
palette() const47 QPalette PaletteEditor::palette() const
48 {
49 return m_editPalette;
50 }
51
setPalette(const QPalette & palette)52 void PaletteEditor::setPalette(const QPalette &palette)
53 {
54 m_editPalette = palette;
55 const auto mask = palette.
56 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
57 resolveMask()
58 #else
59 resolve()
60 #endif
61 ;
62 for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
63 if (mask & (1 << i)) {
64 continue;
65 }
66 m_editPalette.setBrush(
67 QPalette::Active, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i)));
68 m_editPalette.setBrush(
69 QPalette::Inactive, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i)));
70 m_editPalette.setBrush(
71 QPalette::Disabled, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i)));
72 }
73 m_editPalette.
74 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
75 setResolveMask(mask);
76 m_editPalette = m_editPalette.resolve(m_editPalette)
77 #else
78 resolve(mask)
79 #endif
80 ;
81 updatePreviewPalette();
82 updateStyledButton();
83 m_paletteUpdated = true;
84 if (!m_modelUpdated) {
85 m_paletteModel->setPalette(m_editPalette, m_parentPalette);
86 }
87 m_paletteUpdated = false;
88 }
89
setPalette(const QPalette & palette,const QPalette & parentPalette)90 void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette)
91 {
92 m_parentPalette = parentPalette;
93 setPalette(palette);
94 }
95
handleBuildButtonColorChanged(const QColor &)96 void PaletteEditor::handleBuildButtonColorChanged(const QColor &)
97 {
98 buildPalette();
99 }
100
handleActiveRadioClicked()101 void PaletteEditor::handleActiveRadioClicked()
102 {
103 m_currentColorGroup = QPalette::Active;
104 updatePreviewPalette();
105 }
106
handleInactiveRadioClicked()107 void PaletteEditor::handleInactiveRadioClicked()
108 {
109 m_currentColorGroup = QPalette::Inactive;
110 updatePreviewPalette();
111 }
112
handleDisabledRadioClicked()113 void PaletteEditor::handleDisabledRadioClicked()
114 {
115 m_currentColorGroup = QPalette::Disabled;
116 updatePreviewPalette();
117 }
118
handleComputeRadioClicked()119 void PaletteEditor::handleComputeRadioClicked()
120 {
121 if (m_compute) {
122 return;
123 }
124 m_ui->paletteView->setColumnHidden(2, true);
125 m_ui->paletteView->setColumnHidden(3, true);
126 m_compute = true;
127 m_paletteModel->setCompute(true);
128 }
129
handleDetailsRadioClicked()130 void PaletteEditor::handleDetailsRadioClicked()
131 {
132 if (!m_compute) {
133 return;
134 }
135 const int w = m_ui->paletteView->columnWidth(1);
136 m_ui->paletteView->setColumnHidden(2, false);
137 m_ui->paletteView->setColumnHidden(3, false);
138 auto *const header = m_ui->paletteView->header();
139 header->resizeSection(1, w / 3);
140 header->resizeSection(2, w / 3);
141 header->resizeSection(3, w / 3);
142 m_compute = false;
143 m_paletteModel->setCompute(false);
144 }
145
paletteChanged(const QPalette & palette)146 void PaletteEditor::paletteChanged(const QPalette &palette)
147 {
148 m_modelUpdated = true;
149 if (!m_paletteUpdated) {
150 setPalette(palette);
151 }
152 m_modelUpdated = false;
153 }
154
buildPalette()155 void PaletteEditor::buildPalette()
156 {
157 const QColor btn(m_ui->buildButton->color());
158 const QPalette temp(btn);
159 setPalette(temp);
160 }
161
updatePreviewPalette()162 void PaletteEditor::updatePreviewPalette()
163 {
164 const QPalette::ColorGroup g = currentColorGroup();
165 // build the preview palette
166 const QPalette currentPalette = palette();
167 QPalette previewPalette;
168 for (int i = QPalette::WindowText; i < QPalette::NColorRoles; ++i) {
169 const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(i);
170 const QBrush br = currentPalette.brush(g, r);
171 previewPalette.setBrush(QPalette::Active, r, br);
172 previewPalette.setBrush(QPalette::Inactive, r, br);
173 previewPalette.setBrush(QPalette::Disabled, r, br);
174 }
175 }
176
updateStyledButton()177 void PaletteEditor::updateStyledButton()
178 {
179 m_ui->buildButton->setColor(palette().color(QPalette::Active, QPalette::Button));
180 }
181
getPalette(QWidget * parent,const QPalette & init,const QPalette & parentPal,int * ok)182 QPalette PaletteEditor::getPalette(QWidget *parent, const QPalette &init, const QPalette &parentPal, int *ok)
183 {
184 PaletteEditor dlg(parent);
185 auto parentPalette(parentPal);
186 const auto mask = init.
187 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
188 resolveMask()
189 #else
190 resolve()
191 #endif
192 ;
193 for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
194 if (mask & (1 << i)) {
195 continue;
196 }
197 parentPalette.setBrush(
198 QPalette::Active, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i)));
199 parentPalette.setBrush(
200 QPalette::Inactive, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i)));
201 parentPalette.setBrush(
202 QPalette::Disabled, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i)));
203 }
204 dlg.setPalette(init, parentPalette);
205
206 const int result = dlg.exec();
207 if (ok) {
208 *ok = result;
209 }
210 return result == QDialog::Accepted ? dlg.palette() : init;
211 }
212
PaletteModel(QObject * parent)213 PaletteModel::PaletteModel(QObject *parent)
214 : QAbstractTableModel(parent)
215 , m_compute(true)
216 {
217 const QMetaObject *meta = metaObject();
218 const QMetaProperty property = meta->property(meta->indexOfProperty("colorRole"));
219 const QMetaEnum enumerator = property.enumerator();
220 for (int r = QPalette::WindowText; r < QPalette::NColorRoles; ++r) {
221 m_roleNames[static_cast<QPalette::ColorRole>(r)] = QLatin1String(enumerator.key(r));
222 }
223 }
224
rowCount(const QModelIndex &) const225 int PaletteModel::rowCount(const QModelIndex &) const
226 {
227 return m_roleNames.count();
228 }
229
columnCount(const QModelIndex &) const230 int PaletteModel::columnCount(const QModelIndex &) const
231 {
232 return 4;
233 }
234
data(const QModelIndex & index,int role) const235 QVariant PaletteModel::data(const QModelIndex &index, int role) const
236 {
237 if (!index.isValid() || index.row() < 0 || index.row() >= QPalette::NColorRoles || index.column() < 0 || index.column() >= 4) {
238 return QVariant();
239 }
240
241 if (index.column() == 0) {
242 if (role == Qt::DisplayRole) {
243 return m_roleNames[static_cast<QPalette::ColorRole>(index.row())];
244 }
245 if (role == Qt::EditRole) {
246 const auto mask = m_palette.
247 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
248 resolveMask()
249 #else
250 resolve()
251 #endif
252 ;
253 if (mask & (1 << index.row()))
254 return true;
255 return false;
256 }
257 return QVariant();
258 }
259 if (role == BrushRole) {
260 return m_palette.brush(columnToGroup(index.column()), static_cast<QPalette::ColorRole>(index.row()));
261 }
262 return QVariant();
263 }
264
setData(const QModelIndex & index,const QVariant & value,int role)265 bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role)
266 {
267 if (!index.isValid()) {
268 return false;
269 }
270
271 if (index.column() != 0 && role == BrushRole) {
272 const QBrush br = qvariant_cast<QBrush>(value);
273 const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(index.row());
274 const QPalette::ColorGroup g = columnToGroup(index.column());
275 m_palette.setBrush(g, r, br);
276
277 QModelIndex idxBegin = PaletteModel::index(r, 0);
278 QModelIndex idxEnd = PaletteModel::index(r, 3);
279 if (m_compute) {
280 m_palette.setBrush(QPalette::Inactive, r, br);
281 switch (r) {
282 case QPalette::WindowText:
283 case QPalette::Text:
284 case QPalette::ButtonText:
285 case QPalette::Base:
286 break;
287 case QPalette::Dark:
288 m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, br);
289 m_palette.setBrush(QPalette::Disabled, QPalette::Dark, br);
290 m_palette.setBrush(QPalette::Disabled, QPalette::Text, br);
291 m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br);
292 idxBegin = PaletteModel::index(0, 0);
293 idxEnd = PaletteModel::index(m_roleNames.count() - 1, 3);
294 break;
295 case QPalette::Window:
296 m_palette.setBrush(QPalette::Disabled, QPalette::Base, br);
297 m_palette.setBrush(QPalette::Disabled, QPalette::Window, br);
298 idxBegin = PaletteModel::index(QPalette::Base, 0);
299 break;
300 case QPalette::Highlight:
301 break;
302 default:
303 m_palette.setBrush(QPalette::Disabled, r, br);
304 break;
305 }
306 }
307 emit paletteChanged(m_palette);
308 emit dataChanged(idxBegin, idxEnd);
309 return true;
310 }
311 if (index.column() == 0 && role == Qt::EditRole) {
312 auto mask = m_palette.
313 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
314 resolveMask()
315 #else
316 resolve()
317 #endif
318 ;
319 const bool isMask = qvariant_cast<bool>(value);
320 const int r = index.row();
321 if (isMask) {
322 mask |= (1 << r);
323 } else {
324 m_palette.setBrush(
325 QPalette::Active, static_cast<QPalette::ColorRole>(r), m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(r)));
326 m_palette.setBrush(QPalette::Inactive, static_cast<QPalette::ColorRole>(r),
327 m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(r)));
328 m_palette.setBrush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r),
329 m_parentPalette.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r)));
330 mask &= ~static_cast<decltype(mask)>(1 << index.row());
331 }
332 m_palette.
333 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
334 setResolveMask(mask);
335 m_palette = m_palette.resolve(m_palette)
336 #else
337 resolve(mask)
338 #endif
339 ;
340 emit paletteChanged(m_palette);
341 const QModelIndex idxEnd = PaletteModel::index(r, 3);
342 emit dataChanged(index, idxEnd);
343 return true;
344 }
345 return false;
346 }
347
flags(const QModelIndex & index) const348 Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const
349 {
350 if (!index.isValid())
351 return Qt::ItemIsEnabled;
352 return Qt::ItemIsEditable | Qt::ItemIsEnabled;
353 }
354
headerData(int section,Qt::Orientation orientation,int role) const355 QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, int role) const
356 {
357 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
358 if (section == 0)
359 return tr("Color Role");
360 if (section == groupToColumn(QPalette::Active))
361 return tr("Active");
362 if (section == groupToColumn(QPalette::Inactive))
363 return tr("Inactive");
364 if (section == groupToColumn(QPalette::Disabled))
365 return tr("Disabled");
366 }
367 return QVariant();
368 }
369
getPalette() const370 QPalette PaletteModel::getPalette() const
371 {
372 return m_palette;
373 }
374
setPalette(const QPalette & palette,const QPalette & parentPalette)375 void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette)
376 {
377 m_parentPalette = parentPalette;
378 m_palette = palette;
379 const QModelIndex idxBegin = index(0, 0);
380 const QModelIndex idxEnd = index(m_roleNames.count() - 1, 3);
381 emit dataChanged(idxBegin, idxEnd);
382 }
383
columnToGroup(int index) const384 QPalette::ColorGroup PaletteModel::columnToGroup(int index) const
385 {
386 if (index == 1)
387 return QPalette::Active;
388 if (index == 2)
389 return QPalette::Inactive;
390 return QPalette::Disabled;
391 }
392
groupToColumn(QPalette::ColorGroup group) const393 int PaletteModel::groupToColumn(QPalette::ColorGroup group) const
394 {
395 if (group == QPalette::Active)
396 return 1;
397 if (group == QPalette::Inactive)
398 return 2;
399 return 3;
400 }
401
BrushEditor(QWidget * parent)402 BrushEditor::BrushEditor(QWidget *parent)
403 : QWidget(parent)
404 , m_button(new ColorButton(this))
405 , m_changed(false)
406 {
407 auto *const layout = new QHBoxLayout(this);
408 layout->setContentsMargins(0, 0, 0, 0);
409 layout->addWidget(m_button);
410 connect(m_button, &ColorButton::colorChanged, this, &BrushEditor::brushChanged);
411 setFocusProxy(m_button);
412 }
413
setBrush(const QBrush & brush)414 void BrushEditor::setBrush(const QBrush &brush)
415 {
416 m_button->setColor(brush.color());
417 m_changed = false;
418 }
419
brush() const420 QBrush BrushEditor::brush() const
421 {
422 return QBrush(m_button->color());
423 }
424
brushChanged()425 void BrushEditor::brushChanged()
426 {
427 m_changed = true;
428 emit changed(this);
429 }
430
changed() const431 bool BrushEditor::changed() const
432 {
433 return m_changed;
434 }
435
RoleEditor(QWidget * parent)436 RoleEditor::RoleEditor(QWidget *parent)
437 : QWidget(parent)
438 , m_label(new QLabel(this))
439 , m_edited(false)
440 {
441 QHBoxLayout *layout = new QHBoxLayout(this);
442 layout->setContentsMargins(0, 0, 0, 0);
443 layout->setSpacing(0);
444
445 layout->addWidget(m_label);
446 m_label->setAutoFillBackground(true);
447 m_label->setIndent(3); // same value as textMargin in QItemDelegate
448 setFocusProxy(m_label);
449
450 auto *const button = new QToolButton(this);
451 button->setToolButtonStyle(Qt::ToolButtonIconOnly);
452 button->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
453 button->setIconSize(QSize(8, 8));
454 button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding));
455 layout->addWidget(button);
456 connect(button, &QAbstractButton::clicked, this, &RoleEditor::emitResetProperty);
457 }
458
setLabel(const QString & label)459 void RoleEditor::setLabel(const QString &label)
460 {
461 m_label->setText(label);
462 }
463
setEdited(bool on)464 void RoleEditor::setEdited(bool on)
465 {
466 QFont font;
467 if (on == true) {
468 font.setBold(on);
469 }
470 m_label->setFont(font);
471 m_edited = on;
472 }
473
edited() const474 bool RoleEditor::edited() const
475 {
476 return m_edited;
477 }
478
emitResetProperty()479 void RoleEditor::emitResetProperty()
480 {
481 setEdited(false);
482 emit changed(this);
483 }
484
ColorDelegate(QObject * parent)485 ColorDelegate::ColorDelegate(QObject *parent)
486 : QItemDelegate(parent)
487 {
488 }
489
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex & index) const490 QWidget *ColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
491 {
492 if (index.column() == 0) {
493 auto *const editor = new RoleEditor(parent);
494 connect(editor, &RoleEditor::changed, this, &ColorDelegate::commitData);
495 return editor;
496 }
497
498 using BrushEditorWidgetSignal = void (BrushEditor::*)(QWidget *);
499
500 auto *const editor = new BrushEditor(parent);
501 connect(editor, static_cast<BrushEditorWidgetSignal>(&BrushEditor::changed), this, &ColorDelegate::commitData);
502 editor->setFocusPolicy(Qt::NoFocus);
503 editor->installEventFilter(const_cast<ColorDelegate *>(this));
504 return editor;
505 }
506
setEditorData(QWidget * ed,const QModelIndex & index) const507 void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const
508 {
509 if (index.column() == 0) {
510 const auto mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole));
511 auto *const editor = static_cast<RoleEditor *>(ed);
512 editor->setEdited(mask);
513 const auto colorName = qvariant_cast<QString>(index.model()->data(index, Qt::DisplayRole));
514 editor->setLabel(colorName);
515 } else {
516 const auto br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole));
517 auto *const editor = static_cast<BrushEditor *>(ed);
518 editor->setBrush(br);
519 }
520 }
521
setModelData(QWidget * ed,QAbstractItemModel * model,const QModelIndex & index) const522 void ColorDelegate::setModelData(QWidget *ed, QAbstractItemModel *model, const QModelIndex &index) const
523 {
524 if (index.column() == 0) {
525 const auto *const editor = static_cast<RoleEditor *>(ed);
526 const auto mask = editor->edited();
527 model->setData(index, mask, Qt::EditRole);
528 } else {
529 const auto *const editor = static_cast<BrushEditor *>(ed);
530 if (editor->changed()) {
531 QBrush br = editor->brush();
532 model->setData(index, br, BrushRole);
533 }
534 }
535 }
536
updateEditorGeometry(QWidget * ed,const QStyleOptionViewItem & option,const QModelIndex & index) const537 void ColorDelegate::updateEditorGeometry(QWidget *ed, const QStyleOptionViewItem &option, const QModelIndex &index) const
538 {
539 QItemDelegate::updateEditorGeometry(ed, option, index);
540 ed->setGeometry(ed->geometry().adjusted(0, 0, -1, -1));
541 }
542
paint(QPainter * painter,const QStyleOptionViewItem & opt,const QModelIndex & index) const543 void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const
544 {
545 QStyleOptionViewItem option = opt;
546 const auto mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole));
547 if (index.column() == 0 && mask) {
548 option.font.setBold(true);
549 }
550 auto br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole));
551 if (br.style() == Qt::LinearGradientPattern || br.style() == Qt::RadialGradientPattern || br.style() == Qt::ConicalGradientPattern) {
552 painter->save();
553 painter->translate(option.rect.x(), option.rect.y());
554 painter->scale(option.rect.width(), option.rect.height());
555 QGradient gr = *(br.gradient());
556 gr.setCoordinateMode(QGradient::LogicalMode);
557 br = QBrush(gr);
558 painter->fillRect(0, 0, 1, 1, br);
559 painter->restore();
560 } else {
561 painter->save();
562 painter->setBrushOrigin(option.rect.x(), option.rect.y());
563 painter->fillRect(option.rect, br);
564 painter->restore();
565 }
566 QItemDelegate::paint(painter, option, index);
567
568 const QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option));
569 const QPen oldPen = painter->pen();
570 painter->setPen(QPen(color));
571
572 painter->drawLine(option.rect.right(), option.rect.y(), option.rect.right(), option.rect.bottom());
573 painter->drawLine(option.rect.x(), option.rect.bottom(), option.rect.right(), option.rect.bottom());
574 painter->setPen(oldPen);
575 }
576
sizeHint(const QStyleOptionViewItem & opt,const QModelIndex & index) const577 QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const
578 {
579 return QItemDelegate::sizeHint(opt, index) + QSize(4, 4);
580 }
581
582 } // namespace QtUtilities
583