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