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 
59 QT_BEGIN_NAMESPACE
60 
61 namespace qdesigner_internal {
62 
63 enum { BrushRole = 33 };
64 
PaletteEditor(QDesignerFormEditorInterface * core,QWidget * parent)65 PaletteEditor::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(tr("Save..."), QDialogButtonBox::ActionRole);
72     connect(saveButton, &QPushButton::clicked, this, &PaletteEditor::save);
73     auto loadButton = ui.buttonBox->addButton(tr("Load..."), QDialogButtonBox::ActionRole);
74     connect(loadButton, &QPushButton::clicked, this, &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(m_paletteModel, &PaletteModel::paletteChanged,
84                 this, &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(2, true);
90     ui.paletteView->setColumnHidden(3, true);
91     ui.paletteView->setContextMenuPolicy(Qt::CustomContextMenu);
92     connect(ui.paletteView, &QWidget::customContextMenuRequested,
93             this, &PaletteEditor::viewContextMenuRequested);
94 
95     const auto itemRect = ui.paletteView->visualRect(m_paletteModel->index(0, 0));
96     const int minHeight = qMin(itemRect.height() * QPalette::NColorRoles,
97                                (screen()->geometry().height() * 2) / 3);
98     ui.paletteView->setMinimumSize({itemRect.width() * 4, minHeight});
99 }
100 
101 PaletteEditor::~PaletteEditor() = default;
102 
palette() const103 QPalette PaletteEditor::palette() const
104 {
105     return m_editPalette;
106 }
107 
setPalette(const QPalette & palette)108 void 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(QPalette::Active, static_cast<QPalette::ColorRole>(i),
115                         m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i)));
116             m_editPalette.setBrush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i),
117                         m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i)));
118             m_editPalette.setBrush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i),
119                         m_parentPalette.brush(QPalette::Disabled, 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(m_editPalette, m_parentPalette);
128     m_paletteUpdated = false;
129 }
130 
setPalette(const QPalette & palette,const QPalette & parentPalette)131 void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette)
132 {
133     m_parentPalette = parentPalette;
134     setPalette(palette);
135 }
136 
on_buildButton_colorChanged(const QColor &)137 void PaletteEditor::on_buildButton_colorChanged(const QColor &)
138 {
139     buildPalette();
140 }
141 
on_activeRadio_clicked()142 void PaletteEditor::on_activeRadio_clicked()
143 {
144     m_currentColorGroup = QPalette::Active;
145     updatePreviewPalette();
146 }
147 
on_inactiveRadio_clicked()148 void PaletteEditor::on_inactiveRadio_clicked()
149 {
150     m_currentColorGroup = QPalette::Inactive;
151     updatePreviewPalette();
152 }
153 
on_disabledRadio_clicked()154 void PaletteEditor::on_disabledRadio_clicked()
155 {
156     m_currentColorGroup = QPalette::Disabled;
157     updatePreviewPalette();
158 }
159 
on_computeRadio_clicked()160 void PaletteEditor::on_computeRadio_clicked()
161 {
162     if (m_compute)
163         return;
164     ui.paletteView->setColumnHidden(2, true);
165     ui.paletteView->setColumnHidden(3, true);
166     m_compute = true;
167     m_paletteModel->setCompute(true);
168 }
169 
on_detailsRadio_clicked()170 void PaletteEditor::on_detailsRadio_clicked()
171 {
172     if (!m_compute)
173         return;
174     const int w = ui.paletteView->columnWidth(1);
175     ui.paletteView->setColumnHidden(2, false);
176     ui.paletteView->setColumnHidden(3, false);
177     QHeaderView *header = ui.paletteView->header();
178     header->resizeSection(1, w / 3);
179     header->resizeSection(2, w / 3);
180     header->resizeSection(3, w / 3);
181     m_compute = false;
182     m_paletteModel->setCompute(false);
183 }
184 
paletteChanged(const QPalette & palette)185 void PaletteEditor::paletteChanged(const QPalette &palette)
186 {
187     m_modelUpdated = true;
188     if (!m_paletteUpdated)
189         setPalette(palette);
190     m_modelUpdated = false;
191 }
192 
buildPalette()193 void PaletteEditor::buildPalette()
194 {
195     const QColor btn = ui.buildButton->color();
196     const QPalette temp = QPalette(btn);
197     setPalette(temp);
198 }
199 
updatePreviewPalette()200 void 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(g, r);
209         previewPalette.setBrush(QPalette::Active, r, br);
210         previewPalette.setBrush(QPalette::Inactive, r, br);
211         previewPalette.setBrush(QPalette::Disabled, r, 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 
updateStyledButton()220 void PaletteEditor::updateStyledButton()
221 {
222     ui.buildButton->setColor(palette().color(QPalette::Active, QPalette::Button));
223 }
224 
getPalette(QDesignerFormEditorInterface * core,QWidget * parent,const QPalette & init,const QPalette & parentPal,int * ok)225 QPalette 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(QPalette::Active, static_cast<QPalette::ColorRole>(i),
234                         init.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i)));
235             parentPalette.setBrush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i),
236                         init.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i)));
237             parentPalette.setBrush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i),
238                         init.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i)));
239         }
240     }
241     dlg.setPalette(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 
viewContextMenuRequested(const QPoint & pos)249 void PaletteEditor::viewContextMenuRequested(const QPoint &pos)
250 {
251     const auto index = ui.paletteView->indexAt(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(tr("Lighter"));
259         m_darkerAction = m_contextMenu->addAction(tr("Darker"));
260         m_copyColorAction = m_contextMenu->addAction(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("Copy color %1").arg(color.name()));
267     auto action = m_contextMenu->exec(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(factor)
281         : (isBlack ? QColor(0x404040u) : color.lighter(factor));
282     brush.setColor(newColor);
283     m_paletteModel->setData(index, QVariant(brush), BrushRole);
284 }
285 
paletteSuffix()286 static inline QString paletteSuffix() { return QStringLiteral("xml"); }
287 
paletteFilter()288 static inline QString paletteFilter()
289 {
290     return PaletteEditor::tr("QPalette UI file (*.xml)");
291 }
292 
savePalette(const QString & fileName,const QPalette & pal,QString * errorMessage)293 static bool savePalette(const QString &fileName, const QPalette &pal, QString *errorMessage)
294 {
295     QSaveFile file;
296     file.setFileName(fileName);
297     if (!file.open(QIODevice::WriteOnly)) {
298         *errorMessage = PaletteEditor::tr("Cannot open %1 for writing: %2")
299                         .arg(QDir::toNativeSeparators(fileName), file.errorString());
300         return false;
301     }
302     {
303         QScopedPointer<DomPalette> domPalette(QFormBuilderExtra::savePalette(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("Cannot write %1: %2")
314                         .arg(QDir::toNativeSeparators(fileName), file.errorString());
315     }
316     return result;
317 }
318 
msgCannotReadPalette(const QString & fileName,const QXmlStreamReader & reader,const QString & why)319 static QString msgCannotReadPalette(const QString &fileName, const QXmlStreamReader &reader,
320                                     const QString &why)
321 {
322     return PaletteEditor::tr("Cannot read palette from %1:%2:%3")
323            .arg(QDir::toNativeSeparators(fileName)).arg(reader.lineNumber()).arg(why);
324 }
325 
msgCannotReadPalette(const QString & fileName,const QXmlStreamReader & reader)326 static inline QString msgCannotReadPalette(const QString &fileName, const QXmlStreamReader &reader)
327 {
328     return msgCannotReadPalette(fileName, reader, reader.errorString());
329 }
330 
loadPalette(const QString & fileName,QPalette * pal,QString * errorMessage)331 static bool loadPalette(const QString &fileName, QPalette *pal, QString *errorMessage)
332 {
333     QFile file(fileName);
334     if (!file.open(QIODevice::ReadOnly)) {
335         *errorMessage = PaletteEditor::tr("Cannot open %1 for reading: %2")
336                         .arg(QDir::toNativeSeparators(fileName), 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("Invalid element \"%1\", expected \"palette\".")
346                          .arg(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(domPalette.data());
357     return true;
358 }
359 
save()360 void PaletteEditor::save()
361 {
362     QFileDialog dialog(this, tr("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(dialog.selectedFiles().constFirst(), palette(), &errorMessage))
368             break;
369         QMessageBox::warning(this, tr("Error Writing Palette"), errorMessage);
370     }
371 }
372 
load()373 void PaletteEditor::load()
374 {
375     QFileDialog dialog(this, tr("Load Palette"), QString(), paletteFilter());
376     dialog.setAcceptMode(QFileDialog::AcceptOpen);
377     while (dialog.exec() == QDialog::Accepted) {
378         QPalette pal;
379         QString errorMessage;
380         if (loadPalette(dialog.selectedFiles().constFirst(), &pal, &errorMessage)) {
381             setPalette(pal);
382             break;
383         }
384         QMessageBox::warning(this, tr("Error Reading Palette"), errorMessage);
385     }
386 }
387 
388 //////////////////////
389 
PaletteModel(QObject * parent)390 PaletteModel::PaletteModel(QObject *parent)  :
391     QAbstractTableModel(parent)
392 {
393     const QMetaObject *meta = metaObject();
394     const int index = meta->indexOfProperty("colorRole");
395     const QMetaProperty p = meta->property(index);
396     const QMetaEnum e = p.enumerator();
397     m_roleEntries.reserve(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({QLatin1String(e.key(r)), role});
402     }
403 }
404 
rowCount(const QModelIndex &) const405 int PaletteModel::rowCount(const QModelIndex &) const
406 {
407     return m_roleEntries.size();
408 }
409 
columnCount(const QModelIndex &) const410 int PaletteModel::columnCount(const QModelIndex &) const
411 {
412     return 4;
413 }
414 
brushAt(const QModelIndex & index) const415 QBrush PaletteModel::brushAt(const QModelIndex &index) const
416 {
417     return m_palette.brush(columnToGroup(index.column()), roleAt(index.row()));
418 }
419 
data(const QModelIndex & index,int role) const420 QVariant 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(index.row()).name;
432         if (role == Qt::EditRole) {
433             const uint mask = m_palette.resolve();
434             if (mask & (1 << int(roleAt(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 
setData(const QModelIndex & index,const QVariant & value,int role)447 bool 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>(value);
457         const QPalette::ColorGroup g = columnToGroup(index.column());
458         m_palette.setBrush(g, colorRole, br);
459 
460         QModelIndex idxBegin = PaletteModel::index(row, 0);
461         QModelIndex idxEnd = PaletteModel::index(row, 3);
462         if (m_compute) {
463             m_palette.setBrush(QPalette::Inactive, colorRole, 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(QPalette::Disabled, QPalette::WindowText, br);
472                     m_palette.setBrush(QPalette::Disabled, QPalette::Dark, br);
473                     m_palette.setBrush(QPalette::Disabled, QPalette::Text, br);
474                     m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br);
475                     idxBegin = PaletteModel::index(0, 0);
476                     idxEnd = PaletteModel::index(m_roleEntries.size() - 1, 3);
477                     break;
478                 case QPalette::Window:
479                     m_palette.setBrush(QPalette::Disabled, QPalette::Base, br);
480                     m_palette.setBrush(QPalette::Disabled, QPalette::Window, br);
481                     idxBegin = PaletteModel::index(rowOf(QPalette::Base), 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(QPalette::Disabled, colorRole, br);
488                     break;
489             }
490         }
491         emit paletteChanged(m_palette);
492         emit dataChanged(idxBegin, 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>(value);
498         if (isMask)
499             mask |= (1 << int(colorRole));
500         else {
501             m_palette.setBrush(QPalette::Active, colorRole,
502                                m_parentPalette.brush(QPalette::Active, colorRole));
503             m_palette.setBrush(QPalette::Inactive, colorRole,
504                                m_parentPalette.brush(QPalette::Inactive, colorRole));
505             m_palette.setBrush(QPalette::Disabled, colorRole,
506                                m_parentPalette.brush(QPalette::Disabled, colorRole));
507 
508             mask &= ~(1 << int(colorRole));
509         }
510         m_palette.resolve(mask);
511         emit paletteChanged(m_palette);
512         const QModelIndex idxEnd = PaletteModel::index(row, 3);
513         emit dataChanged(index, idxEnd);
514         return true;
515     }
516     return false;
517 }
518 
flags(const QModelIndex & index) const519 Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const
520 {
521     if (!index.isValid())
522         return Qt::ItemIsEnabled;
523     return Qt::ItemIsEditable | Qt::ItemIsEnabled;
524 }
525 
headerData(int section,Qt::Orientation orientation,int role) const526 QVariant 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("Color Role");
532         if (section == groupToColumn(QPalette::Active))
533             return tr("Active");
534         if (section == groupToColumn(QPalette::Inactive))
535             return tr("Inactive");
536         if (section == groupToColumn(QPalette::Disabled))
537             return tr("Disabled");
538     }
539     return QVariant();
540 }
541 
getPalette() const542 QPalette PaletteModel::getPalette() const
543 {
544     return m_palette;
545 }
546 
setPalette(const QPalette & palette,const QPalette & parentPalette)547 void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette)
548 {
549     m_parentPalette = parentPalette;
550     m_palette = palette;
551     const QModelIndex idxBegin = index(0, 0);
552     const QModelIndex idxEnd = index(m_roleEntries.size() - 1, 3);
553     emit dataChanged(idxBegin, idxEnd);
554 }
555 
columnToGroup(int index) const556 QPalette::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 
groupToColumn(QPalette::ColorGroup group) const565 int 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 
rowOf(QPalette::ColorRole role) const574 int PaletteModel::rowOf(QPalette::ColorRole role) const
575 {
576     for (int row = 0, size = m_roleEntries.size(); row < size; ++row) {
577         if (m_roleEntries.at(row).role == role)
578             return row;
579     }
580     return -1;
581 }
582 
583 //////////////////////////
584 
BrushEditor(QDesignerFormEditorInterface * core,QWidget * parent)585 BrushEditor::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(m_button);
593     connect(m_button, &QtColorButton::colorChanged, this, &BrushEditor::brushChanged);
594     setFocusProxy(m_button);
595 }
596 
setBrush(const QBrush & brush)597 void BrushEditor::setBrush(const QBrush &brush)
598 {
599     m_button->setColor(brush.color());
600     m_changed = false;
601 }
602 
brush() const603 QBrush BrushEditor::brush() const
604 {
605     return QBrush(m_button->color());
606 }
607 
brushChanged()608 void BrushEditor::brushChanged()
609 {
610     m_changed = true;
611     emit changed(this);
612 }
613 
changed() const614 bool BrushEditor::changed() const
615 {
616     return m_changed;
617 }
618 
619 //////////////////////////
620 
RoleEditor(QWidget * parent)621 RoleEditor::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(button, &QAbstractButton::clicked, this, &RoleEditor::emitResetProperty);
641 }
642 
setLabel(const QString & label)643 void RoleEditor::setLabel(const QString &label)
644 {
645     m_label->setText(label);
646 }
647 
setEdited(bool on)648 void 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 
edited() const657 bool RoleEditor::edited() const
658 {
659     return m_edited;
660 }
661 
emitResetProperty()662 void RoleEditor::emitResetProperty()
663 {
664     setEdited(false);
665     emit changed(this);
666 }
667 
668 //////////////////////////
ColorDelegate(QDesignerFormEditorInterface * core,QObject * parent)669 ColorDelegate::ColorDelegate(QDesignerFormEditorInterface *core, QObject *parent) :
670     QItemDelegate(parent),
671     m_core(core)
672 {
673 }
674 
createEditor(QWidget * parent,const QStyleOptionViewItem &,const QModelIndex & index) const675 QWidget *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(editor, &RoleEditor::changed, this, &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(editor, QOverload<QWidget *>::of(&BrushEditor::changed),
688                 this, &ColorDelegate::commitData);
689         editor->setFocusPolicy(Qt::NoFocus);
690         editor->installEventFilter(const_cast<ColorDelegate *>(this));
691         ed = editor;
692     }
693     return ed;
694 }
695 
setEditorData(QWidget * ed,const QModelIndex & index) const696 void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const
697 {
698     if (index.column() == 0) {
699         const bool mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole));
700         RoleEditor *editor = static_cast<RoleEditor *>(ed);
701         editor->setEdited(mask);
702         const QString colorName = qvariant_cast<QString>(index.model()->data(index, Qt::DisplayRole));
703         editor->setLabel(colorName);
704     } else {
705         const QBrush br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole));
706         BrushEditor *editor = static_cast<BrushEditor *>(ed);
707         editor->setBrush(br);
708     }
709 }
710 
setModelData(QWidget * ed,QAbstractItemModel * model,const QModelIndex & index) const711 void 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, mask, Qt::EditRole);
718     } else {
719         BrushEditor *editor = static_cast<BrushEditor *>(ed);
720         if (editor->changed()) {
721             QBrush br = editor->brush();
722             model->setData(index, br, BrushRole);
723         }
724     }
725 }
726 
updateEditorGeometry(QWidget * ed,const QStyleOptionViewItem & option,const QModelIndex & index) const727 void ColorDelegate::updateEditorGeometry(QWidget *ed,
728                 const QStyleOptionViewItem &option, const QModelIndex &index) const
729 {
730     QItemDelegate::updateEditorGeometry(ed, option, index);
731     ed->setGeometry(ed->geometry().adjusted(0, 0, -1, -1));
732 }
733 
paint(QPainter * painter,const QStyleOptionViewItem & opt,const QModelIndex & index) const734 void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt,
735             const QModelIndex &index) const
736 {
737     QStyleOptionViewItem option = opt;
738     const bool mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole));
739     if (index.column() == 0 && mask) {
740         option.font.setBold(true);
741     }
742     QBrush br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole));
743     if (br.style() == Qt::LinearGradientPattern ||
744             br.style() == Qt::RadialGradientPattern ||
745             br.style() == Qt::ConicalGradientPattern) {
746         painter->save();
747         painter->translate(option.rect.x(), option.rect.y());
748         painter->scale(option.rect.width(), option.rect.height());
749         QGradient gr = *(br.gradient());
750         gr.setCoordinateMode(QGradient::LogicalMode);
751         br = QBrush(gr);
752         painter->fillRect(0, 0, 1, 1, br);
753         painter->restore();
754     } else {
755         painter->save();
756         painter->setBrushOrigin(option.rect.x(), 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(QStyle::SH_Table_GridLineColor, &option));
764     const QPen oldPen = painter->pen();
765     painter->setPen(QPen(color));
766 
767     painter->drawLine(option.rect.right(), option.rect.y(),
768             option.rect.right(), option.rect.bottom());
769     painter->drawLine(option.rect.x(), option.rect.bottom(),
770             option.rect.right(), option.rect.bottom());
771     painter->setPen(oldPen);
772 }
773 
sizeHint(const QStyleOptionViewItem & opt,const QModelIndex & index) const774 QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const
775 {
776     return QItemDelegate::sizeHint(opt, index) + QSize(4, 4);
777 }
778 }
779 
780 QT_END_NAMESPACE
781