1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "paletteeditor.h"
30
31#include <iconloader_p.h>
32#include <qtcolorbutton.h>
33
34#include <private/formbuilderextra_p.h>
35#include <private/ui4_p.h>
36
37#include <QtDesigner/abstractformeditor.h>
38#include <QtDesigner/abstractformwindowmanager.h>
39
40#include <QtCore/qfile.h>
41#include <QtCore/qmetaobject.h>
42#include <QtCore/qsavefile.h>
43#include <QtCore/qxmlstream.h>
44#include <QtGui/qguiapplication.h>
45#include <QtGui/qpainter.h>
46#include <QtGui/qscreen.h>
47#if QT_CONFIG(clipboard)
48# include <QtGui/qclipboard.h>
49#endif
50#include <QtWidgets/qaction.h>
51#include <QtWidgets/qfiledialog.h>
52#include <QtWidgets/qmessagebox.h>
53#include <QtWidgets/qpushbutton.h>
54#include <QtWidgets/qtoolbutton.h>
55#include <QtWidgets/qlabel.h>
56#include <QtWidgets/qmenu.h>
57#include <QtWidgets/qheaderview.h>
58
59QT_BEGIN_NAMESPACE
60
61namespace qdesigner_internal {
62
63enum { BrushRole = 33 };
64
65PaletteEditor::PaletteEditor(QDesignerFormEditorInterface *core, QWidget *parent) :
66 QDialog(parent),
67 m_paletteModel(new PaletteModel(this)),
68 m_core(core)
69{
70 ui.setupUi(this);
71 auto saveButton = ui.buttonBox->addButton(text: tr(s: "Save..."), role: QDialogButtonBox::ActionRole);
72 connect(sender: saveButton, signal: &QPushButton::clicked, receiver: this, slot: &PaletteEditor::save);
73 auto loadButton = ui.buttonBox->addButton(text: tr(s: "Load..."), role: QDialogButtonBox::ActionRole);
74 connect(sender: loadButton, signal: &QPushButton::clicked, receiver: this, slot: &PaletteEditor::load);
75
76 ui.paletteView->setModel(m_paletteModel);
77 updatePreviewPalette();
78 updateStyledButton();
79 ui.paletteView->setModel(m_paletteModel);
80 ColorDelegate *delegate = new ColorDelegate(core, this);
81 ui.paletteView->setItemDelegate(delegate);
82 ui.paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers);
83 connect(sender: m_paletteModel, signal: &PaletteModel::paletteChanged,
84 receiver: this, slot: &PaletteEditor::paletteChanged);
85 ui.paletteView->setSelectionBehavior(QAbstractItemView::SelectRows);
86 ui.paletteView->setDragEnabled(true);
87 ui.paletteView->setDropIndicatorShown(true);
88 ui.paletteView->setRootIsDecorated(false);
89 ui.paletteView->setColumnHidden(column: 2, hide: true);
90 ui.paletteView->setColumnHidden(column: 3, hide: true);
91 ui.paletteView->setContextMenuPolicy(Qt::CustomContextMenu);
92 connect(sender: ui.paletteView, signal: &QWidget::customContextMenuRequested,
93 receiver: this, slot: &PaletteEditor::viewContextMenuRequested);
94
95 const auto itemRect = ui.paletteView->visualRect(index: m_paletteModel->index(row: 0, column: 0));
96 const int minHeight = qMin(a: itemRect.height() * QPalette::NColorRoles,
97 b: (screen()->geometry().height() * 2) / 3);
98 ui.paletteView->setMinimumSize({itemRect.width() * 4, minHeight});
99}
100
101PaletteEditor::~PaletteEditor() = default;
102
103QPalette PaletteEditor::palette() const
104{
105 return m_editPalette;
106}
107
108void PaletteEditor::setPalette(const QPalette &palette)
109{
110 m_editPalette = palette;
111 const uint mask = palette.resolve();
112 for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
113 if (!(mask & (1 << i))) {
114 m_editPalette.setBrush(cg: QPalette::Active, cr: static_cast<QPalette::ColorRole>(i),
115 brush: m_parentPalette.brush(cg: QPalette::Active, cr: static_cast<QPalette::ColorRole>(i)));
116 m_editPalette.setBrush(cg: QPalette::Inactive, cr: static_cast<QPalette::ColorRole>(i),
117 brush: m_parentPalette.brush(cg: QPalette::Inactive, cr: static_cast<QPalette::ColorRole>(i)));
118 m_editPalette.setBrush(cg: QPalette::Disabled, cr: static_cast<QPalette::ColorRole>(i),
119 brush: m_parentPalette.brush(cg: QPalette::Disabled, cr: static_cast<QPalette::ColorRole>(i)));
120 }
121 }
122 m_editPalette.resolve(mask);
123 updatePreviewPalette();
124 updateStyledButton();
125 m_paletteUpdated = true;
126 if (!m_modelUpdated)
127 m_paletteModel->setPalette(palette: m_editPalette, parentPalette: m_parentPalette);
128 m_paletteUpdated = false;
129}
130
131void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette)
132{
133 m_parentPalette = parentPalette;
134 setPalette(palette);
135}
136
137void PaletteEditor::on_buildButton_colorChanged(const QColor &)
138{
139 buildPalette();
140}
141
142void PaletteEditor::on_activeRadio_clicked()
143{
144 m_currentColorGroup = QPalette::Active;
145 updatePreviewPalette();
146}
147
148void PaletteEditor::on_inactiveRadio_clicked()
149{
150 m_currentColorGroup = QPalette::Inactive;
151 updatePreviewPalette();
152}
153
154void PaletteEditor::on_disabledRadio_clicked()
155{
156 m_currentColorGroup = QPalette::Disabled;
157 updatePreviewPalette();
158}
159
160void PaletteEditor::on_computeRadio_clicked()
161{
162 if (m_compute)
163 return;
164 ui.paletteView->setColumnHidden(column: 2, hide: true);
165 ui.paletteView->setColumnHidden(column: 3, hide: true);
166 m_compute = true;
167 m_paletteModel->setCompute(true);
168}
169
170void PaletteEditor::on_detailsRadio_clicked()
171{
172 if (!m_compute)
173 return;
174 const int w = ui.paletteView->columnWidth(column: 1);
175 ui.paletteView->setColumnHidden(column: 2, hide: false);
176 ui.paletteView->setColumnHidden(column: 3, hide: false);
177 QHeaderView *header = ui.paletteView->header();
178 header->resizeSection(logicalIndex: 1, size: w / 3);
179 header->resizeSection(logicalIndex: 2, size: w / 3);
180 header->resizeSection(logicalIndex: 3, size: w / 3);
181 m_compute = false;
182 m_paletteModel->setCompute(false);
183}
184
185void PaletteEditor::paletteChanged(const QPalette &palette)
186{
187 m_modelUpdated = true;
188 if (!m_paletteUpdated)
189 setPalette(palette);
190 m_modelUpdated = false;
191}
192
193void PaletteEditor::buildPalette()
194{
195 const QColor btn = ui.buildButton->color();
196 const QPalette temp = QPalette(btn);
197 setPalette(temp);
198}
199
200void PaletteEditor::updatePreviewPalette()
201{
202 const QPalette::ColorGroup g = currentColorGroup();
203 // build the preview palette
204 const QPalette currentPalette = palette();
205 QPalette previewPalette;
206 for (int i = QPalette::WindowText; i < QPalette::NColorRoles; i++) {
207 const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(i);
208 const QBrush &br = currentPalette.brush(cg: g, cr: r);
209 previewPalette.setBrush(cg: QPalette::Active, cr: r, brush: br);
210 previewPalette.setBrush(cg: QPalette::Inactive, cr: r, brush: br);
211 previewPalette.setBrush(cg: QPalette::Disabled, cr: r, brush: br);
212 }
213 ui.previewFrame->setPreviewPalette(previewPalette);
214
215 const bool enabled = g != QPalette::Disabled;
216 ui.previewFrame->setEnabled(enabled);
217 ui.previewFrame->setSubWindowActive(g != QPalette::Inactive);
218}
219
220void PaletteEditor::updateStyledButton()
221{
222 ui.buildButton->setColor(palette().color(cg: QPalette::Active, cr: QPalette::Button));
223}
224
225QPalette PaletteEditor::getPalette(QDesignerFormEditorInterface *core, QWidget* parent, const QPalette &init,
226 const QPalette &parentPal, int *ok)
227{
228 PaletteEditor dlg(core, parent);
229 QPalette parentPalette(parentPal);
230 uint mask = init.resolve();
231 for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) {
232 if (!(mask & (1 << i))) {
233 parentPalette.setBrush(cg: QPalette::Active, cr: static_cast<QPalette::ColorRole>(i),
234 brush: init.brush(cg: QPalette::Active, cr: static_cast<QPalette::ColorRole>(i)));
235 parentPalette.setBrush(cg: QPalette::Inactive, cr: static_cast<QPalette::ColorRole>(i),
236 brush: init.brush(cg: QPalette::Inactive, cr: static_cast<QPalette::ColorRole>(i)));
237 parentPalette.setBrush(cg: QPalette::Disabled, cr: static_cast<QPalette::ColorRole>(i),
238 brush: init.brush(cg: QPalette::Disabled, cr: static_cast<QPalette::ColorRole>(i)));
239 }
240 }
241 dlg.setPalette(palette: init, parentPalette);
242
243 const int result = dlg.exec();
244 if (ok) *ok = result;
245
246 return result == QDialog::Accepted ? dlg.palette() : init;
247}
248
249void PaletteEditor::viewContextMenuRequested(const QPoint &pos)
250{
251 const auto index = ui.paletteView->indexAt(p: pos);
252 if (!index.isValid())
253 return;
254 auto brush = m_paletteModel->brushAt(index);
255 const auto color = brush.color();
256 if (!m_contextMenu) {
257 m_contextMenu = new QMenu(this);
258 m_lighterAction = m_contextMenu->addAction(text: tr(s: "Lighter"));
259 m_darkerAction = m_contextMenu->addAction(text: tr(s: "Darker"));
260 m_copyColorAction = m_contextMenu->addAction(text: QString());
261 }
262 const auto rgb = color.rgb() & 0xffffffu;
263 const bool isBlack = rgb == 0u;
264 m_lighterAction->setEnabled(rgb != 0xffffffu);
265 m_darkerAction->setDisabled(isBlack);
266 m_copyColorAction->setText(tr(s: "Copy color %1").arg(a: color.name()));
267 auto action = m_contextMenu->exec(pos: ui.paletteView->viewport()->mapToGlobal(pos));
268 if (!action)
269 return;
270 if (action == m_copyColorAction) {
271#if QT_CONFIG(clipboard)
272 QGuiApplication::clipboard()->setText(color.name());
273#endif
274 return;
275 }
276 // Fall through to darker/lighter. Note: black cannot be made lighter due
277 // to QTBUG-9343.
278 enum : int { factor = 120 };
279 const QColor newColor = action == m_darkerAction
280 ? color.darker(f: factor)
281 : (isBlack ? QColor(0x404040u) : color.lighter(f: factor));
282 brush.setColor(newColor);
283 m_paletteModel->setData(index, value: QVariant(brush), role: BrushRole);
284}
285
286static inline QString paletteSuffix() { return QStringLiteral("xml"); }
287
288static inline QString paletteFilter()
289{
290 return PaletteEditor::tr(s: "QPalette UI file (*.xml)");
291}
292
293static bool savePalette(const QString &fileName, const QPalette &pal, QString *errorMessage)
294{
295 QSaveFile file;
296 file.setFileName(fileName);
297 if (!file.open(flags: QIODevice::WriteOnly)) {
298 *errorMessage = PaletteEditor::tr(s: "Cannot open %1 for writing: %2")
299 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString());
300 return false;
301 }
302 {
303 QScopedPointer<DomPalette> domPalette(QFormBuilderExtra::savePalette(palette: pal));
304 QXmlStreamWriter writer(&file);
305 writer.setAutoFormatting(true);
306 writer.setAutoFormattingIndent(1);
307 writer.writeStartDocument();
308 domPalette->write(writer);
309 writer.writeEndDocument();
310 }
311 const bool result = file.commit();
312 if (!result) {
313 *errorMessage = PaletteEditor::tr(s: "Cannot write %1: %2")
314 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString());
315 }
316 return result;
317}
318
319static QString msgCannotReadPalette(const QString &fileName, const QXmlStreamReader &reader,
320 const QString &why)
321{
322 return PaletteEditor::tr(s: "Cannot read palette from %1:%2:%3")
323 .arg(a: QDir::toNativeSeparators(pathName: fileName)).arg(a: reader.lineNumber()).arg(a: why);
324}
325
326static inline QString msgCannotReadPalette(const QString &fileName, const QXmlStreamReader &reader)
327{
328 return msgCannotReadPalette(fileName, reader, why: reader.errorString());
329}
330
331static bool loadPalette(const QString &fileName, QPalette *pal, QString *errorMessage)
332{
333 QFile file(fileName);
334 if (!file.open(flags: QIODevice::ReadOnly)) {
335 *errorMessage = PaletteEditor::tr(s: "Cannot open %1 for reading: %2")
336 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString());
337 return false;
338 }
339 QXmlStreamReader reader(&file);
340 if (!reader.readNextStartElement()) {
341 *errorMessage = msgCannotReadPalette(fileName, reader);
342 return false;
343 }
344 if (reader.name() != QLatin1String("palette")) {
345 const auto why = PaletteEditor::tr(s: "Invalid element \"%1\", expected \"palette\".")
346 .arg(a: reader.name().toString());
347 *errorMessage = msgCannotReadPalette(fileName, reader, why);
348 return false;
349 }
350 QScopedPointer<DomPalette> domPalette(new DomPalette);
351 domPalette->read(reader);
352 if (reader.hasError()) {
353 *errorMessage = msgCannotReadPalette(fileName, reader);
354 return false;
355 }
356 *pal = QFormBuilderExtra::loadPalette(dom: domPalette.data());
357 return true;
358}
359
360void PaletteEditor::save()
361{
362 QFileDialog dialog(this, tr(s: "Save Palette"), QString(), paletteFilter());
363 dialog.setAcceptMode(QFileDialog::AcceptSave);
364 dialog.setDefaultSuffix(paletteSuffix());
365 while (dialog.exec() == QDialog::Accepted) {
366 QString errorMessage;
367 if (savePalette(fileName: dialog.selectedFiles().constFirst(), pal: palette(), errorMessage: &errorMessage))
368 break;
369 QMessageBox::warning(parent: this, title: tr(s: "Error Writing Palette"), text: errorMessage);
370 }
371}
372
373void PaletteEditor::load()
374{
375 QFileDialog dialog(this, tr(s: "Load Palette"), QString(), paletteFilter());
376 dialog.setAcceptMode(QFileDialog::AcceptOpen);
377 while (dialog.exec() == QDialog::Accepted) {
378 QPalette pal;
379 QString errorMessage;
380 if (loadPalette(fileName: dialog.selectedFiles().constFirst(), pal: &pal, errorMessage: &errorMessage)) {
381 setPalette(pal);
382 break;
383 }
384 QMessageBox::warning(parent: this, title: tr(s: "Error Reading Palette"), text: errorMessage);
385 }
386}
387
388//////////////////////
389
390PaletteModel::PaletteModel(QObject *parent) :
391 QAbstractTableModel(parent)
392{
393 const QMetaObject *meta = metaObject();
394 const int index = meta->indexOfProperty(name: "colorRole");
395 const QMetaProperty p = meta->property(index);
396 const QMetaEnum e = p.enumerator();
397 m_roleEntries.reserve(asize: QPalette::NColorRoles);
398 for (int r = QPalette::WindowText; r < QPalette::NColorRoles; r++) {
399 const auto role = static_cast<QPalette::ColorRole>(r);
400 if (role != QPalette::NoRole)
401 m_roleEntries.append(t: {.name: QLatin1String(e.key(index: r)), .role: role});
402 }
403}
404
405int PaletteModel::rowCount(const QModelIndex &) const
406{
407 return m_roleEntries.size();
408}
409
410int PaletteModel::columnCount(const QModelIndex &) const
411{
412 return 4;
413}
414
415QBrush PaletteModel::brushAt(const QModelIndex &index) const
416{
417 return m_palette.brush(cg: columnToGroup(index: index.column()), cr: roleAt(row: index.row()));
418}
419
420QVariant PaletteModel::data(const QModelIndex &index, int role) const
421{
422 if (!index.isValid())
423 return QVariant();
424 if (index.row() < 0 || index.row() >= m_roleEntries.size())
425 return QVariant();
426 if (index.column() < 0 || index.column() >= 4)
427 return QVariant();
428
429 if (index.column() == 0) {
430 if (role == Qt::DisplayRole)
431 return m_roleEntries.at(i: index.row()).name;
432 if (role == Qt::EditRole) {
433 const uint mask = m_palette.resolve();
434 if (mask & (1 << int(roleAt(row: index.row()))))
435 return true;
436 return false;
437 }
438 return QVariant();
439 }
440 if (role == Qt::ToolTipRole)
441 return brushAt(index).color().name();
442 if (role == BrushRole)
443 return brushAt(index);
444 return QVariant();
445}
446
447bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role)
448{
449 if (!index.isValid())
450 return false;
451
452 const int row = index.row();
453 const auto colorRole = roleAt(row);
454
455 if (index.column() != 0 && role == BrushRole) {
456 const QBrush br = qvariant_cast<QBrush>(v: value);
457 const QPalette::ColorGroup g = columnToGroup(index: index.column());
458 m_palette.setBrush(cg: g, cr: colorRole, brush: br);
459
460 QModelIndex idxBegin = PaletteModel::index(row, column: 0);
461 QModelIndex idxEnd = PaletteModel::index(row, column: 3);
462 if (m_compute) {
463 m_palette.setBrush(cg: QPalette::Inactive, cr: colorRole, brush: br);
464 switch (colorRole) {
465 case QPalette::WindowText:
466 case QPalette::Text:
467 case QPalette::ButtonText:
468 case QPalette::Base:
469 break;
470 case QPalette::Dark:
471 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::WindowText, brush: br);
472 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Dark, brush: br);
473 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Text, brush: br);
474 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::ButtonText, brush: br);
475 idxBegin = PaletteModel::index(row: 0, column: 0);
476 idxEnd = PaletteModel::index(row: m_roleEntries.size() - 1, column: 3);
477 break;
478 case QPalette::Window:
479 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Base, brush: br);
480 m_palette.setBrush(cg: QPalette::Disabled, cr: QPalette::Window, brush: br);
481 idxBegin = PaletteModel::index(row: rowOf(role: QPalette::Base), column: 0);
482 break;
483 case QPalette::Highlight:
484 //m_palette.setBrush(QPalette::Disabled, QPalette::Highlight, c.dark(120));
485 break;
486 default:
487 m_palette.setBrush(cg: QPalette::Disabled, cr: colorRole, brush: br);
488 break;
489 }
490 }
491 emit paletteChanged(palette: m_palette);
492 emit dataChanged(topLeft: idxBegin, bottomRight: idxEnd);
493 return true;
494 }
495 if (index.column() == 0 && role == Qt::EditRole) {
496 uint mask = m_palette.resolve();
497 const bool isMask = qvariant_cast<bool>(v: value);
498 if (isMask)
499 mask |= (1 << int(colorRole));
500 else {
501 m_palette.setBrush(cg: QPalette::Active, cr: colorRole,
502 brush: m_parentPalette.brush(cg: QPalette::Active, cr: colorRole));
503 m_palette.setBrush(cg: QPalette::Inactive, cr: colorRole,
504 brush: m_parentPalette.brush(cg: QPalette::Inactive, cr: colorRole));
505 m_palette.setBrush(cg: QPalette::Disabled, cr: colorRole,
506 brush: m_parentPalette.brush(cg: QPalette::Disabled, cr: colorRole));
507
508 mask &= ~(1 << int(colorRole));
509 }
510 m_palette.resolve(mask);
511 emit paletteChanged(palette: m_palette);
512 const QModelIndex idxEnd = PaletteModel::index(row, column: 3);
513 emit dataChanged(topLeft: index, bottomRight: idxEnd);
514 return true;
515 }
516 return false;
517}
518
519Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const
520{
521 if (!index.isValid())
522 return Qt::ItemIsEnabled;
523 return Qt::ItemIsEditable | Qt::ItemIsEnabled;
524}
525
526QVariant PaletteModel::headerData(int section, Qt::Orientation orientation,
527 int role) const
528{
529 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
530 if (section == 0)
531 return tr(s: "Color Role");
532 if (section == groupToColumn(group: QPalette::Active))
533 return tr(s: "Active");
534 if (section == groupToColumn(group: QPalette::Inactive))
535 return tr(s: "Inactive");
536 if (section == groupToColumn(group: QPalette::Disabled))
537 return tr(s: "Disabled");
538 }
539 return QVariant();
540}
541
542QPalette PaletteModel::getPalette() const
543{
544 return m_palette;
545}
546
547void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette)
548{
549 m_parentPalette = parentPalette;
550 m_palette = palette;
551 const QModelIndex idxBegin = index(row: 0, column: 0);
552 const QModelIndex idxEnd = index(row: m_roleEntries.size() - 1, column: 3);
553 emit dataChanged(topLeft: idxBegin, bottomRight: idxEnd);
554}
555
556QPalette::ColorGroup PaletteModel::columnToGroup(int index) const
557{
558 if (index == 1)
559 return QPalette::Active;
560 if (index == 2)
561 return QPalette::Inactive;
562 return QPalette::Disabled;
563}
564
565int PaletteModel::groupToColumn(QPalette::ColorGroup group) const
566{
567 if (group == QPalette::Active)
568 return 1;
569 if (group == QPalette::Inactive)
570 return 2;
571 return 3;
572}
573
574int PaletteModel::rowOf(QPalette::ColorRole role) const
575{
576 for (int row = 0, size = m_roleEntries.size(); row < size; ++row) {
577 if (m_roleEntries.at(i: row).role == role)
578 return row;
579 }
580 return -1;
581}
582
583//////////////////////////
584
585BrushEditor::BrushEditor(QDesignerFormEditorInterface *core, QWidget *parent) :
586 QWidget(parent),
587 m_button(new QtColorButton(this)),
588 m_core(core)
589{
590 QLayout *layout = new QHBoxLayout(this);
591 layout->setContentsMargins(QMargins());
592 layout->addWidget(w: m_button);
593 connect(sender: m_button, signal: &QtColorButton::colorChanged, receiver: this, slot: &BrushEditor::brushChanged);
594 setFocusProxy(m_button);
595}
596
597void BrushEditor::setBrush(const QBrush &brush)
598{
599 m_button->setColor(brush.color());
600 m_changed = false;
601}
602
603QBrush BrushEditor::brush() const
604{
605 return QBrush(m_button->color());
606}
607
608void BrushEditor::brushChanged()
609{
610 m_changed = true;
611 emit changed(widget: this);
612}
613
614bool BrushEditor::changed() const
615{
616 return m_changed;
617}
618
619//////////////////////////
620
621RoleEditor::RoleEditor(QWidget *parent) :
622 QWidget(parent),
623 m_label(new QLabel(this))
624{
625 QHBoxLayout *layout = new QHBoxLayout(this);
626 layout->setContentsMargins(QMargins());
627 layout->setSpacing(0);
628
629 layout->addWidget(m_label);
630 m_label->setAutoFillBackground(true);
631 m_label->setIndent(3); // ### hardcode it should have the same value of textMargin in QItemDelegate
632 setFocusProxy(m_label);
633
634 QToolButton *button = new QToolButton(this);
635 button->setToolButtonStyle(Qt::ToolButtonIconOnly);
636 button->setIcon(createIconSet(QStringLiteral("resetproperty.png")));
637 button->setIconSize(QSize(8,8));
638 button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding));
639 layout->addWidget(button);
640 connect(sender: button, signal: &QAbstractButton::clicked, receiver: this, slot: &RoleEditor::emitResetProperty);
641}
642
643void RoleEditor::setLabel(const QString &label)
644{
645 m_label->setText(label);
646}
647
648void RoleEditor::setEdited(bool on)
649{
650 QFont font;
651 if (on)
652 font.setBold(on);
653 m_label->setFont(font);
654 m_edited = on;
655}
656
657bool RoleEditor::edited() const
658{
659 return m_edited;
660}
661
662void RoleEditor::emitResetProperty()
663{
664 setEdited(false);
665 emit changed(widget: this);
666}
667
668//////////////////////////
669ColorDelegate::ColorDelegate(QDesignerFormEditorInterface *core, QObject *parent) :
670 QItemDelegate(parent),
671 m_core(core)
672{
673}
674
675QWidget *ColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &,
676 const QModelIndex &index) const
677{
678 QWidget *ed = nullptr;
679 if (index.column() == 0) {
680 RoleEditor *editor = new RoleEditor(parent);
681 connect(sender: editor, signal: &RoleEditor::changed, receiver: this, slot: &ColorDelegate::commitData);
682 //editor->setFocusPolicy(Qt::NoFocus);
683 //editor->installEventFilter(const_cast<ColorDelegate *>(this));
684 ed = editor;
685 } else {
686 BrushEditor *editor = new BrushEditor(m_core, parent);
687 connect(sender: editor, signal: QOverload<QWidget *>::of(ptr: &BrushEditor::changed),
688 receiver: this, slot: &ColorDelegate::commitData);
689 editor->setFocusPolicy(Qt::NoFocus);
690 editor->installEventFilter(filterObj: const_cast<ColorDelegate *>(this));
691 ed = editor;
692 }
693 return ed;
694}
695
696void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const
697{
698 if (index.column() == 0) {
699 const bool mask = qvariant_cast<bool>(v: index.model()->data(index, role: Qt::EditRole));
700 RoleEditor *editor = static_cast<RoleEditor *>(ed);
701 editor->setEdited(mask);
702 const QString colorName = qvariant_cast<QString>(v: index.model()->data(index, role: Qt::DisplayRole));
703 editor->setLabel(colorName);
704 } else {
705 const QBrush br = qvariant_cast<QBrush>(v: index.model()->data(index, role: BrushRole));
706 BrushEditor *editor = static_cast<BrushEditor *>(ed);
707 editor->setBrush(br);
708 }
709}
710
711void ColorDelegate::setModelData(QWidget *ed, QAbstractItemModel *model,
712 const QModelIndex &index) const
713{
714 if (index.column() == 0) {
715 RoleEditor *editor = static_cast<RoleEditor *>(ed);
716 const bool mask = editor->edited();
717 model->setData(index, value: mask, role: Qt::EditRole);
718 } else {
719 BrushEditor *editor = static_cast<BrushEditor *>(ed);
720 if (editor->changed()) {
721 QBrush br = editor->brush();
722 model->setData(index, value: br, role: BrushRole);
723 }
724 }
725}
726
727void ColorDelegate::updateEditorGeometry(QWidget *ed,
728 const QStyleOptionViewItem &option, const QModelIndex &index) const
729{
730 QItemDelegate::updateEditorGeometry(editor: ed, option, index);
731 ed->setGeometry(ed->geometry().adjusted(xp1: 0, yp1: 0, xp2: -1, yp2: -1));
732}
733
734void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt,
735 const QModelIndex &index) const
736{
737 QStyleOptionViewItem option = opt;
738 const bool mask = qvariant_cast<bool>(v: index.model()->data(index, role: Qt::EditRole));
739 if (index.column() == 0 && mask) {
740 option.font.setBold(true);
741 }
742 QBrush br = qvariant_cast<QBrush>(v: index.model()->data(index, role: BrushRole));
743 if (br.style() == Qt::LinearGradientPattern ||
744 br.style() == Qt::RadialGradientPattern ||
745 br.style() == Qt::ConicalGradientPattern) {
746 painter->save();
747 painter->translate(dx: option.rect.x(), dy: option.rect.y());
748 painter->scale(sx: option.rect.width(), sy: option.rect.height());
749 QGradient gr = *(br.gradient());
750 gr.setCoordinateMode(QGradient::LogicalMode);
751 br = QBrush(gr);
752 painter->fillRect(x: 0, y: 0, w: 1, h: 1, b: br);
753 painter->restore();
754 } else {
755 painter->save();
756 painter->setBrushOrigin(x: option.rect.x(), y: option.rect.y());
757 painter->fillRect(option.rect, br);
758 painter->restore();
759 }
760 QItemDelegate::paint(painter, option, index);
761
762
763 const QColor color = static_cast<QRgb>(QApplication::style()->styleHint(stylehint: QStyle::SH_Table_GridLineColor, opt: &option));
764 const QPen oldPen = painter->pen();
765 painter->setPen(QPen(color));
766
767 painter->drawLine(x1: option.rect.right(), y1: option.rect.y(),
768 x2: option.rect.right(), y2: option.rect.bottom());
769 painter->drawLine(x1: option.rect.x(), y1: option.rect.bottom(),
770 x2: option.rect.right(), y2: option.rect.bottom());
771 painter->setPen(oldPen);
772}
773
774QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const
775{
776 return QItemDelegate::sizeHint(option: opt, index) + QSize(4, 4);
777}
778}
779
780QT_END_NAMESPACE
781

source code of qttools/src/designer/src/components/propertyeditor/paletteeditor.cpp