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 | |
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(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 | |
101 | PaletteEditor::~PaletteEditor() = default; |
102 | |
103 | QPalette PaletteEditor::palette() const |
104 | { |
105 | return m_editPalette; |
106 | } |
107 | |
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(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 | |
131 | void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette) |
132 | { |
133 | m_parentPalette = parentPalette; |
134 | setPalette(palette); |
135 | } |
136 | |
137 | void PaletteEditor::on_buildButton_colorChanged(const QColor &) |
138 | { |
139 | buildPalette(); |
140 | } |
141 | |
142 | void PaletteEditor::on_activeRadio_clicked() |
143 | { |
144 | m_currentColorGroup = QPalette::Active; |
145 | updatePreviewPalette(); |
146 | } |
147 | |
148 | void PaletteEditor::on_inactiveRadio_clicked() |
149 | { |
150 | m_currentColorGroup = QPalette::Inactive; |
151 | updatePreviewPalette(); |
152 | } |
153 | |
154 | void PaletteEditor::on_disabledRadio_clicked() |
155 | { |
156 | m_currentColorGroup = QPalette::Disabled; |
157 | updatePreviewPalette(); |
158 | } |
159 | |
160 | void 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 | |
170 | void 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 * = 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 | |
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 | |
193 | void PaletteEditor::buildPalette() |
194 | { |
195 | const QColor btn = ui.buildButton->color(); |
196 | const QPalette temp = QPalette(btn); |
197 | setPalette(temp); |
198 | } |
199 | |
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(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 | |
220 | void PaletteEditor::updateStyledButton() |
221 | { |
222 | ui.buildButton->setColor(palette().color(cg: QPalette::Active, cr: QPalette::Button)); |
223 | } |
224 | |
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(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 | |
249 | void PaletteEditor::(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 | |
286 | static inline QString paletteSuffix() { return QStringLiteral("xml" ); } |
287 | |
288 | static inline QString paletteFilter() |
289 | { |
290 | return PaletteEditor::tr(s: "QPalette UI file (*.xml)" ); |
291 | } |
292 | |
293 | static 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 | |
319 | static 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 | |
326 | static inline QString msgCannotReadPalette(const QString &fileName, const QXmlStreamReader &reader) |
327 | { |
328 | return msgCannotReadPalette(fileName, reader, why: reader.errorString()); |
329 | } |
330 | |
331 | static 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 | |
360 | void 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 | |
373 | void 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 | |
390 | PaletteModel::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 | |
405 | int PaletteModel::rowCount(const QModelIndex &) const |
406 | { |
407 | return m_roleEntries.size(); |
408 | } |
409 | |
410 | int PaletteModel::columnCount(const QModelIndex &) const |
411 | { |
412 | return 4; |
413 | } |
414 | |
415 | QBrush PaletteModel::brushAt(const QModelIndex &index) const |
416 | { |
417 | return m_palette.brush(cg: columnToGroup(index: index.column()), cr: roleAt(row: index.row())); |
418 | } |
419 | |
420 | 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(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 | |
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>(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 | |
519 | 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 | |
526 | QVariant PaletteModel::(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 | |
542 | QPalette PaletteModel::getPalette() const |
543 | { |
544 | return m_palette; |
545 | } |
546 | |
547 | void 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 | |
556 | 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 | |
565 | 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 | |
574 | 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(i: row).role == role) |
578 | return row; |
579 | } |
580 | return -1; |
581 | } |
582 | |
583 | ////////////////////////// |
584 | |
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(w: m_button); |
593 | connect(sender: m_button, signal: &QtColorButton::colorChanged, receiver: this, slot: &BrushEditor::brushChanged); |
594 | setFocusProxy(m_button); |
595 | } |
596 | |
597 | void BrushEditor::setBrush(const QBrush &brush) |
598 | { |
599 | m_button->setColor(brush.color()); |
600 | m_changed = false; |
601 | } |
602 | |
603 | QBrush BrushEditor::brush() const |
604 | { |
605 | return QBrush(m_button->color()); |
606 | } |
607 | |
608 | void BrushEditor::brushChanged() |
609 | { |
610 | m_changed = true; |
611 | emit changed(widget: this); |
612 | } |
613 | |
614 | bool BrushEditor::changed() const |
615 | { |
616 | return m_changed; |
617 | } |
618 | |
619 | ////////////////////////// |
620 | |
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(sender: button, signal: &QAbstractButton::clicked, receiver: this, slot: &RoleEditor::emitResetProperty); |
641 | } |
642 | |
643 | void RoleEditor::setLabel(const QString &label) |
644 | { |
645 | m_label->setText(label); |
646 | } |
647 | |
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 | |
657 | bool RoleEditor::edited() const |
658 | { |
659 | return m_edited; |
660 | } |
661 | |
662 | void RoleEditor::emitResetProperty() |
663 | { |
664 | setEdited(false); |
665 | emit changed(widget: this); |
666 | } |
667 | |
668 | ////////////////////////// |
669 | ColorDelegate::ColorDelegate(QDesignerFormEditorInterface *core, QObject *parent) : |
670 | QItemDelegate(parent), |
671 | m_core(core) |
672 | { |
673 | } |
674 | |
675 | 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(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 | |
696 | void 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 | |
711 | 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, 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 | |
727 | void 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 | |
734 | 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>(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 | |
774 | QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const |
775 | { |
776 | return QItemDelegate::sizeHint(option: opt, index) + QSize(4, 4); |
777 | } |
778 | } |
779 | |
780 | QT_END_NAMESPACE |
781 | |