Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
iconselector.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
8#include "iconloader_p.h"
10
11#include <abstractdialoggui_p.h>
12#include <QtDesigner/abstractformeditor.h>
13#include <QtDesigner/abstractresourcebrowser.h>
14#include <QtDesigner/abstractlanguage.h>
15#include <QtDesigner/abstractintegration.h>
16#include <QtDesigner/qextensionmanager.h>
17#include <QtDesigner/private/resourcebuilder_p.h>
18
19#include <QtWidgets/qabstractitemview.h>
20#include <QtWidgets/qtoolbutton.h>
21#include <QtWidgets/qcombobox.h>
22#include <QtWidgets/qdialogbuttonbox.h>
23#include <QtWidgets/qpushbutton.h>
24#include <QtWidgets/qdialog.h>
25#include <QtWidgets/qmenu.h>
26#include <QtWidgets/qapplication.h>
27#include <QtWidgets/qboxlayout.h>
28#include <QtGui/qimagereader.h>
29#include <QtWidgets/qdialogbuttonbox.h>
30#include <QtWidgets/qlineedit.h>
31#include <QtWidgets/qlabel.h>
32
33#include <QtGui/qaction.h>
34#include <QtGui/qvalidator.h>
35
36#include <QtCore/qdebug.h>
37#include <QtCore/qlist.h>
38
39#include <utility>
40
41QT_BEGIN_NAMESPACE
42
43using namespace Qt::StringLiterals;
44
45namespace qdesigner_internal {
46
48
50{
51 static QList<ThemeIconEnumEntry> result;
52 if (result.isEmpty()) {
53 const QStringList &names = QResourceBuilder::themeIconNames();
54 result.reserve(names.size());
55 for (qsizetype i = 0, size = names.size(); i < size; ++i)
56 result.append({names.at(i), QIcon::fromTheme(QIcon::ThemeIcon(i))});
57 }
58 return result;
59}
60
61static void initThemeCombo(QComboBox *cb)
62{
63 cb->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
64
65 for (const auto &te : themeEnumIcons())
66 cb->addItem(te.second, te.first);
67
68 cb->setCurrentIndex(-1);
69}
70
71// Validator for theme line edit, accepts empty or non-blank strings.
73public:
74 explicit BlankSuppressingValidator(QObject * parent = nullptr) : QValidator(parent) {}
75 State validate(QString &input, int &pos) const override
76 {
77 const auto blankPos = input.indexOf(u' ');
78 if (blankPos != -1) {
79 pos = blankPos;
80 return Invalid;
81 }
82 return Acceptable;
83 }
84};
85
86// -------------------- LanguageResourceDialogPrivate
88 LanguageResourceDialog *q_ptr;
89 Q_DECLARE_PUBLIC(LanguageResourceDialog)
90
91public:
93 void init(LanguageResourceDialog *p);
94
95 void setCurrentPath(const QString &filePath);
96 QString currentPath() const;
97
99 void slotPathChanged(const QString &);
100
101private:
102 void setOkButtonEnabled(bool v) { m_dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(v); }
103 static bool checkPath(const QString &p);
104
105 QDesignerResourceBrowserInterface *m_browser;
106 QDialogButtonBox *m_dialogButtonBox;
107};
108
109LanguageResourceDialogPrivate::LanguageResourceDialogPrivate(QDesignerResourceBrowserInterface *rb) :
110 q_ptr(nullptr),
111 m_browser(rb),
112 m_dialogButtonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel))
113{
114 setOkButtonEnabled(false);
115}
116
133
134void LanguageResourceDialogPrivate::setCurrentPath(const QString &filePath)
135{
136 m_browser->setCurrentPath(filePath);
137 setOkButtonEnabled(checkPath(filePath));
138}
139
141{
142 return m_browser->currentPath();
143}
144
145bool LanguageResourceDialogPrivate::checkPath(const QString &p)
146{
147 return p.isEmpty() ? false : IconSelector::checkPixmap(p, IconSelector::CheckFast);
148}
149
151{
152 if (checkPath(currentPath()))
153 q_ptr->accept();
154}
155
157{
158 setOkButtonEnabled(checkPath(p));
159}
160
161// ------------ LanguageResourceDialog
165{
166 d_ptr->init( this);
167}
168
170
175
180
190
191// ------------ IconSelectorPrivate
192
194{
196 const char *name;
197};
198
200 {{QIcon::Normal, QIcon::Off}, QT_TRANSLATE_NOOP("IconSelector", "Normal Off")},
201 {{QIcon::Normal, QIcon::On}, QT_TRANSLATE_NOOP("IconSelector", "Normal On")},
202 {{QIcon::Disabled, QIcon::Off}, QT_TRANSLATE_NOOP("IconSelector", "Disabled Off")},
203 {{QIcon::Disabled, QIcon::On}, QT_TRANSLATE_NOOP("IconSelector", "Disabled On")},
204 {{QIcon::Active, QIcon::Off}, QT_TRANSLATE_NOOP("IconSelector", "Active Off")},
205 {{QIcon::Active, QIcon::On}, QT_TRANSLATE_NOOP("IconSelector", "Active On")},
206 {{QIcon::Selected, QIcon::Off}, QT_TRANSLATE_NOOP("IconSelector", "Selected Off")},
207 {{QIcon::Selected, QIcon::On}, QT_TRANSLATE_NOOP("IconSelector", "Selected On")}
208};
209
210constexpr int stateToNameSize = int(sizeof(stateToName) / sizeof(stateToName[0]));
211
213{
214 IconSelector *q_ptr = nullptr;
215 Q_DECLARE_PUBLIC(IconSelector)
216public:
218
226
228 {
229 const int i = m_stateComboBox->currentIndex();
230 return i >= 0 && i < stateToNameSize
231 ? stateToName[i].state : std::pair<QIcon::Mode, QIcon::State>{};
232 }
233
234 const QIcon m_emptyIcon;
235 QComboBox *m_stateComboBox = nullptr;
239 PropertySheetIconValue m_icon;
240 DesignerIconCache *m_iconCache = nullptr;
241 QtResourceModel *m_resourceModel = nullptr;
242 QDesignerFormEditorInterface *m_core = nullptr;
243};
244
246{
247 QIcon icon;
248 if (m_iconCache)
249 icon = m_iconCache->icon(m_icon);
250
251 const auto &paths = m_icon.paths();
252 for (int index = 0; index < stateToNameSize; ++index) {
253 const auto &state = stateToName[index].state;
254 const PropertySheetPixmapValue pixmap = paths.value(state);
255 QIcon pixmapIcon = QIcon(icon.pixmap(16, 16, state.first, state.second));
256 if (pixmapIcon.isNull())
257 pixmapIcon = m_emptyIcon;
258 m_stateComboBox->setItemIcon(index, pixmapIcon);
259 QFont font = q_ptr->font();
260 if (!pixmap.path().isEmpty())
261 font.setBold(true);
262 m_stateComboBox->setItemData(index, font, Qt::FontRole);
263 }
264
265 PropertySheetPixmapValue currentPixmap = paths.value(currentState());
266 m_resetAction->setEnabled(!currentPixmap.path().isEmpty());
267 m_resetAllAction->setEnabled(!paths.isEmpty());
268 m_stateComboBox->update();
269}
270
275
277{
278 const auto state = currentState();
279 const PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
280 // Default to resource
281 const PropertySheetPixmapValue::PixmapSource ps = pixmap.path().isEmpty() ? PropertySheetPixmapValue::ResourcePixmap : pixmap.pixmapSource(m_core);
282 switch (ps) {
283 case PropertySheetPixmapValue::LanguageResourcePixmap:
284 case PropertySheetPixmapValue::ResourcePixmap:
286 break;
287 case PropertySheetPixmapValue::FilePixmap:
289 break;
290 }
291}
292
293// Choose a pixmap from resource; use language-dependent resource browser if present
314
316{
317 const auto state = currentState();
318
319 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
320 const QString oldPath = pixmap.path();
321 const QString newPath = IconSelector::choosePixmapResource(m_core, m_resourceModel, oldPath, q_ptr);
322 if (newPath.isEmpty() || newPath == oldPath)
323 return;
324 const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath);
325 if (newPixmap != pixmap) {
326 m_icon.setPixmap(state.first, state.second, newPixmap);
328 emit q_ptr->iconChanged(m_icon);
329 }
330}
331
332// Helpers for choosing image files: Check for valid image.
334{
335 const QFileInfo fi(fileName);
336 if (!fi.exists() || !fi.isFile() || !fi.isReadable()) {
337 if (errorMessage)
338 *errorMessage = tr("The pixmap file '%1' cannot be read.").arg(fileName);
339 return false;
340 }
342 if (!reader.canRead()) {
343 if (errorMessage)
344 *errorMessage = tr("The file '%1' does not appear to be a valid pixmap file: %2")
346 return false;
347 }
348 if (cm == CheckFast)
349 return true;
350
351 const QImage image = reader.read();
352 if (image.isNull()) {
353 if (errorMessage)
354 *errorMessage = tr("The file '%1' could not be read: %2")
356 return false;
357 }
358 return true;
359}
360
361// Helpers for choosing image files: Return an image filter for QFileDialog, courtesy of StyledButton
362static QString imageFilter()
363{
364 QString filter = QApplication::translate("IconSelector", "All Pixmaps (");
365 const auto supportedImageFormats = QImageReader::supportedImageFormats();
366 const qsizetype count = supportedImageFormats.size();
367 for (qsizetype i = 0; i < count; ++i) {
368 if (i)
369 filter += u' ';
370 filter += "*."_L1;
371 const QString outputFormat = QString::fromUtf8(supportedImageFormats.at(i));
372 if (outputFormat != "JPEG"_L1)
373 filter += outputFormat.toLower();
374 else
375 filter += "jpg *.jpeg"_L1;
376 }
377 filter += u')';
378 return filter;
379}
380
381// Helpers for choosing image files: Choose a file
398
400{
401 const auto state = currentState();
402
403 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
404 const QString newPath = IconSelector::choosePixmapFile(pixmap.path(), m_core->dialogGui(), q_ptr);
405 if (!newPath.isEmpty()) {
406 const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath);
407 if (!(newPixmap == pixmap)) {
408 m_icon.setPixmap(state.first, state.second, newPixmap);
410 emit q_ptr->iconChanged(m_icon);
411 }
412 }
413}
414
416{
417 const auto state = currentState();
418
419 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
420 const PropertySheetPixmapValue newPixmap;
421 if (!(newPixmap == pixmap)) {
422 m_icon.setPixmap(state.first, state.second, newPixmap);
424 emit q_ptr->iconChanged(m_icon);
425 }
426}
427
429{
430 const PropertySheetIconValue newIcon;
431 if (!(m_icon == newIcon)) {
432 m_icon = newIcon;
434 emit q_ptr->iconChanged(m_icon);
435 }
436}
437
438// ------------- IconSelector
441{
442 d_ptr->q_ptr = this;
443
444 d_ptr->m_stateComboBox = new QComboBox(this);
445
446 QHBoxLayout *l = new QHBoxLayout(this);
447 d_ptr->m_iconButton = new QToolButton(this);
448 d_ptr->m_iconButton->setText(tr("..."));
453
454 QMenu *setMenu = new QMenu(this);
455
456 QAction *setResourceAction = new QAction(tr("Choose Resource..."), this);
457 QAction *setFileAction = new QAction(tr("Choose File..."), this);
458 d_ptr->m_resetAction = new QAction(tr("Reset"), this);
459 d_ptr->m_resetAllAction = new QAction(tr("Reset All"), this);
462 //d_ptr->m_resetAction->setIcon(createIconSet("resetproperty.png"_L1));
463
469
470 for (const auto &item : stateToName)
472
474
476 this, [this] { d_ptr->slotStateActivated(); });
478 this, [this] { d_ptr->slotSetActivated(); });
480 this, [this] { d_ptr->slotSetResourceActivated(); });
482 this, [this] { d_ptr->slotSetFileActivated(); });
484 this, [this] { d_ptr->slotResetActivated(); });
486 this, [this] { d_ptr->slotResetAllActivated(); });
487 d_ptr->slotUpdate();
488}
489
490IconSelector::~IconSelector() = default;
491
493{
494 if (d_ptr->m_icon == icon)
495 return;
496
497 d_ptr->m_icon = icon;
498 d_ptr->slotUpdate();
499}
500
502{
503 return d_ptr->m_icon;
504}
505
512
519
520// --- IconThemeEditor
521
522static const QMap<QString, QIcon> &themeIcons()
523{
524 static QMap<QString, QIcon> result;
525 if (result.isEmpty()) {
526 QFile file(u":/qt-project.org/designer/icon-naming-spec.txt"_s);
527 if (file.open(QIODevice::ReadOnly)) {
528 while (!file.atEnd()) {
529 const auto line = file.readLine().trimmed();
530 if (line.isEmpty() || line.startsWith('#'))
531 continue;
532 const auto iconName = QString::fromUtf8(line);
533 result.insert(iconName, QIcon::fromTheme(iconName));
534 }
535 file.close();
536 }
537 }
538 return result;
539}
540
542 void create(QWidget *topLevel, bool wantResetButton);
543
544 QComboBox *m_themeComboBox{};
546};
547
548void IconThemeEditorPrivate::create(QWidget *topLevel, bool wantResetButton)
549{
550 m_themeComboBox = new QComboBox();
551 QHBoxLayout *mainHLayout = new QHBoxLayout(topLevel);
552 mainHLayout->setContentsMargins({});
553 mainHLayout->addWidget(m_themeComboBox);
554 if (wantResetButton) {
555 m_themeResetButton = new QToolButton;
556 m_themeResetButton->setIcon(createIconSet("resetproperty.png"_L1));
557 mainHLayout->addWidget(m_themeResetButton);
558 }
559 topLevel->setFocusProxy(m_themeComboBox);
560}
561
577
578IconThemeEditor::~IconThemeEditor() = default;
579
585
587{
588 return d->m_themeComboBox->currentText();
589}
590
595
607
609
615
617{
618 return d->m_themeComboBox->currentIndex();
619}
620
626
631
638
639} // qdesigner_internal
640
641QT_END_NAMESPACE
642
643#include "moc_iconselector_p.cpp"
friend class QWidget
Definition qpainter.h:432
State validate(QString &input, int &pos) const override
This virtual function returns \l Invalid if input is invalid according to this validator's rules,...
BlankSuppressingValidator(QObject *parent=nullptr)
std::pair< QIcon::Mode, QIcon::State > currentState() const
QDesignerFormEditorInterface * m_core
Auxiliary methods to store/retrieve settings.
static const QList< ThemeIconEnumEntry > & themeEnumIcons()
static void initThemeCombo(QComboBox *cb)
constexpr QIconStateName stateToName[]
static const QMap< QString, QIcon > & themeIcons()
constexpr int stateToNameSize
static QString imageFilter()
void create(QWidget *topLevel, bool wantResetButton)
std::pair< QIcon::Mode, QIcon::State > state