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 DesignerPixmapCache *m_pixmapCache = nullptr;
242 QtResourceModel *m_resourceModel = nullptr;
243 QDesignerFormEditorInterface *m_core = nullptr;
244};
245
247{
248 QIcon icon;
249 if (m_iconCache)
250 icon = m_iconCache->icon(m_icon);
251
252 const auto &paths = m_icon.paths();
253 for (int index = 0; index < stateToNameSize; ++index) {
254 const auto &state = stateToName[index].state;
255 const PropertySheetPixmapValue pixmap = paths.value(state);
256 QIcon pixmapIcon = QIcon(icon.pixmap(16, 16, state.first, state.second));
257 if (pixmapIcon.isNull())
258 pixmapIcon = m_emptyIcon;
259 m_stateComboBox->setItemIcon(index, pixmapIcon);
260 QFont font = q_ptr->font();
261 if (!pixmap.path().isEmpty())
262 font.setBold(true);
263 m_stateComboBox->setItemData(index, font, Qt::FontRole);
264 }
265
266 PropertySheetPixmapValue currentPixmap = paths.value(currentState());
267 m_resetAction->setEnabled(!currentPixmap.path().isEmpty());
268 m_resetAllAction->setEnabled(!paths.isEmpty());
269 m_stateComboBox->update();
270}
271
276
278{
279 const auto state = currentState();
280 const PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
281 // Default to resource
282 const PropertySheetPixmapValue::PixmapSource ps = pixmap.path().isEmpty() ? PropertySheetPixmapValue::ResourcePixmap : pixmap.pixmapSource(m_core);
283 switch (ps) {
284 case PropertySheetPixmapValue::LanguageResourcePixmap:
285 case PropertySheetPixmapValue::ResourcePixmap:
287 break;
288 case PropertySheetPixmapValue::FilePixmap:
290 break;
291 }
292}
293
294// Choose a pixmap from resource; use language-dependent resource browser if present
315
317{
318 const auto state = currentState();
319
320 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
321 const QString oldPath = pixmap.path();
322 const QString newPath = IconSelector::choosePixmapResource(m_core, m_resourceModel, oldPath, q_ptr);
323 if (newPath.isEmpty() || newPath == oldPath)
324 return;
325 const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath);
326 if (newPixmap != pixmap) {
327 m_icon.setPixmap(state.first, state.second, newPixmap);
329 emit q_ptr->iconChanged(m_icon);
330 }
331}
332
333// Helpers for choosing image files: Check for valid image.
335{
336 const QFileInfo fi(fileName);
337 if (!fi.exists() || !fi.isFile() || !fi.isReadable()) {
338 if (errorMessage)
339 *errorMessage = tr("The pixmap file '%1' cannot be read.").arg(fileName);
340 return false;
341 }
343 if (!reader.canRead()) {
344 if (errorMessage)
345 *errorMessage = tr("The file '%1' does not appear to be a valid pixmap file: %2")
347 return false;
348 }
349 if (cm == CheckFast)
350 return true;
351
352 const QImage image = reader.read();
353 if (image.isNull()) {
354 if (errorMessage)
355 *errorMessage = tr("The file '%1' could not be read: %2")
357 return false;
358 }
359 return true;
360}
361
362// Helpers for choosing image files: Return an image filter for QFileDialog, courtesy of StyledButton
363static QString imageFilter()
364{
365 QString filter = QApplication::translate("IconSelector", "All Pixmaps (");
366 const auto supportedImageFormats = QImageReader::supportedImageFormats();
367 const qsizetype count = supportedImageFormats.size();
368 for (qsizetype i = 0; i < count; ++i) {
369 if (i)
370 filter += u' ';
371 filter += "*."_L1;
372 const QString outputFormat = QString::fromUtf8(supportedImageFormats.at(i));
373 if (outputFormat != "JPEG"_L1)
374 filter += outputFormat.toLower();
375 else
376 filter += "jpg *.jpeg"_L1;
377 }
378 filter += u')';
379 return filter;
380}
381
382// Helpers for choosing image files: Choose a file
399
401{
402 const auto state = currentState();
403
404 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
405 const QString newPath = IconSelector::choosePixmapFile(pixmap.path(), m_core->dialogGui(), q_ptr);
406 if (!newPath.isEmpty()) {
407 const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath);
408 if (!(newPixmap == pixmap)) {
409 m_icon.setPixmap(state.first, state.second, newPixmap);
411 emit q_ptr->iconChanged(m_icon);
412 }
413 }
414}
415
417{
418 const auto state = currentState();
419
420 PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second);
421 const PropertySheetPixmapValue newPixmap;
422 if (!(newPixmap == pixmap)) {
423 m_icon.setPixmap(state.first, state.second, newPixmap);
425 emit q_ptr->iconChanged(m_icon);
426 }
427}
428
430{
431 const PropertySheetIconValue newIcon;
432 if (!(m_icon == newIcon)) {
433 m_icon = newIcon;
435 emit q_ptr->iconChanged(m_icon);
436 }
437}
438
439// ------------- IconSelector
442{
443 d_ptr->q_ptr = this;
444
445 d_ptr->m_stateComboBox = new QComboBox(this);
446
447 QHBoxLayout *l = new QHBoxLayout(this);
448 d_ptr->m_iconButton = new QToolButton(this);
449 d_ptr->m_iconButton->setText(tr("..."));
454
455 QMenu *setMenu = new QMenu(this);
456
457 QAction *setResourceAction = new QAction(tr("Choose Resource..."), this);
458 QAction *setFileAction = new QAction(tr("Choose File..."), this);
459 d_ptr->m_resetAction = new QAction(tr("Reset"), this);
460 d_ptr->m_resetAllAction = new QAction(tr("Reset All"), this);
463 //d_ptr->m_resetAction->setIcon(createIconSet("resetproperty.png"_L1));
464
470
471 for (const auto &item : stateToName)
473
475
477 this, [this] { d_ptr->slotStateActivated(); });
479 this, [this] { d_ptr->slotSetActivated(); });
481 this, [this] { d_ptr->slotSetResourceActivated(); });
483 this, [this] { d_ptr->slotSetFileActivated(); });
485 this, [this] { d_ptr->slotResetActivated(); });
487 this, [this] { d_ptr->slotResetAllActivated(); });
488 d_ptr->slotUpdate();
489}
490
491IconSelector::~IconSelector() = default;
492
494{
495 if (d_ptr->m_icon == icon)
496 return;
497
498 d_ptr->m_icon = icon;
499 d_ptr->slotUpdate();
500}
501
503{
504 return d_ptr->m_icon;
505}
506
513
520
527
528// --- IconThemeEditor
529
530static const QMap<QString, QIcon> &themeIcons()
531{
532 static QMap<QString, QIcon> result;
533 if (result.isEmpty()) {
534 QFile file(u":/qt-project.org/designer/icon-naming-spec.txt"_s);
535 if (file.open(QIODevice::ReadOnly)) {
536 while (!file.atEnd()) {
537 const auto line = file.readLine().trimmed();
538 if (line.isEmpty() || line.startsWith('#'))
539 continue;
540 const auto iconName = QString::fromUtf8(line);
541 result.insert(iconName, QIcon::fromTheme(iconName));
542 }
543 file.close();
544 }
545 }
546 return result;
547}
548
550 void create(QWidget *topLevel, bool wantResetButton);
551
552 QComboBox *m_themeComboBox{};
554};
555
556void IconThemeEditorPrivate::create(QWidget *topLevel, bool wantResetButton)
557{
558 m_themeComboBox = new QComboBox();
559 QHBoxLayout *mainHLayout = new QHBoxLayout(topLevel);
560 mainHLayout->setContentsMargins({});
561 mainHLayout->addWidget(m_themeComboBox);
562 if (wantResetButton) {
563 m_themeResetButton = new QToolButton;
564 m_themeResetButton->setIcon(createIconSet("resetproperty.png"_L1));
565 mainHLayout->addWidget(m_themeResetButton);
566 }
567 topLevel->setFocusProxy(m_themeComboBox);
568}
569
585
586IconThemeEditor::~IconThemeEditor() = default;
587
593
595{
596 return d->m_themeComboBox->currentText();
597}
598
603
615
617
623
625{
626 return d->m_themeComboBox->currentIndex();
627}
628
634
639
646
647} // qdesigner_internal
648
649QT_END_NAMESPACE
650
651#include "moc_iconselector_p.cpp"
friend class QWidget
Definition qpainter.h:421
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