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
pixmapeditor.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "pixmapeditor.h"
5#include <iconloader_p.h>
6#include <iconselector_p.h>
7#include <qdesigner_utils_p.h>
8
9#include <QtDesigner/abstractformeditor.h>
10
11#include <QtWidgets/qapplication.h>
12#include <QtWidgets/qlabel.h>
13#include <QtWidgets/qtoolbutton.h>
14#include <QtWidgets/qboxlayout.h>
15#include <QtWidgets/qlineedit.h>
16#include <QtWidgets/qdialogbuttonbox.h>
17#include <QtWidgets/qpushbutton.h>
18#include <QtWidgets/qfiledialog.h>
19#include <QtWidgets/qmenu.h>
20
21#include <QtGui/qaction.h>
22#if QT_CONFIG(clipboard)
23#include <QtGui/qclipboard.h>
24#endif
25#include <QtGui/qevent.h>
26
28
29using namespace Qt::StringLiterals;
30
31static constexpr QSize ICON_SIZE{16, 16};
32
33namespace qdesigner_internal {
34
35static void createIconThemeDialog(QDialog *topLevel, const QString &labelText,
36 QWidget *themeEditor)
37{
38 QVBoxLayout *layout = new QVBoxLayout(topLevel);
39 QLabel *label = new QLabel(labelText, topLevel);
40 QDialogButtonBox *buttons = new QDialogButtonBox(topLevel);
41 buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
42 QObject::connect(buttons, &QDialogButtonBox::accepted, topLevel, &QDialog::accept);
43 QObject::connect(buttons, &QDialogButtonBox::rejected, topLevel, &QDialog::reject);
44
45 layout->addWidget(label);
46 layout->addWidget(themeEditor);
47 layout->addWidget(buttons);
48}
49
50IconThemeDialog::IconThemeDialog(QWidget *parent)
51 : QDialog(parent)
52{
53 setWindowTitle(tr("Set Icon From XDG Theme"));
54 m_editor = new IconThemeEditor(this);
55 createIconThemeDialog(this, tr("Select icon name from XDG theme:"), m_editor);
56}
57
58std::optional<QString> IconThemeDialog::getTheme(QWidget *parent, const QString &theme)
59{
60 IconThemeDialog dlg(parent);
61 dlg.m_editor->setTheme(theme);
62 if (dlg.exec() == QDialog::Accepted)
63 return dlg.m_editor->theme();
64 return std::nullopt;
65}
66
67IconThemeEnumDialog::IconThemeEnumDialog(QWidget *parent)
68 : QDialog(parent)
69{
70 setWindowTitle(tr("Set Icon From Theme"));
71 m_editor = new IconThemeEnumEditor(this);
72 createIconThemeDialog(this, tr("Select icon name from theme:"), m_editor);
73}
74
75std::optional<int> IconThemeEnumDialog::getTheme(QWidget *parent, int theme)
76{
77 IconThemeEnumDialog dlg(parent);
78 dlg.m_editor->setThemeEnum(theme);
79 if (dlg.exec() == QDialog::Accepted)
80 return dlg.m_editor->themeEnum();
81 return std::nullopt;
82}
83
84PixmapEditor::PixmapEditor(QDesignerFormEditorInterface *core, QWidget *parent) :
85 QWidget(parent),
86 m_iconThemeModeEnabled(false),
87 m_core(core),
88 m_pixmapLabel(new QLabel(this)),
89 m_pathLabel(new QLabel(this)),
90 m_button(new QToolButton(this)),
91 m_resourceAction(new QAction(tr("Choose Resource..."), this)),
92 m_fileAction(new QAction(tr("Choose File..."), this)),
93 m_themeEnumAction(new QAction(tr("Set Icon From Theme..."), this)),
94 m_themeAction(new QAction(tr("Set Icon From XDG Theme..."), this)),
95 m_copyAction(new QAction(createIconSet(QIcon::ThemeIcon::EditCopy, "editcopy.png"_L1),
96 tr("Copy Path"), this)),
97 m_pasteAction(new QAction(createIconSet(QIcon::ThemeIcon::EditPaste, "editpaste.png"_L1),
98 tr("Paste Path"), this)),
99 m_layout(new QHBoxLayout(this)),
100 m_pixmapCache(nullptr)
101{
102 m_layout->addWidget(m_pixmapLabel);
103 m_layout->addWidget(m_pathLabel);
104 m_button->setText(tr("..."));
105 m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
106 m_button->setFixedWidth(30);
107 m_button->setPopupMode(QToolButton::MenuButtonPopup);
108 m_layout->addWidget(m_button);
109 m_layout->setContentsMargins(QMargins());
110 m_layout->setSpacing(0);
111 m_pixmapLabel->setFixedWidth(ICON_SIZE.width());
112 m_pixmapLabel->setAlignment(Qt::AlignCenter);
113 m_pathLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
114 m_themeAction->setVisible(false);
115 m_themeEnumAction->setVisible(false);
116
117 QMenu *menu = new QMenu(this);
118 menu->addAction(m_resourceAction);
119 menu->addAction(m_fileAction);
120 menu->addAction(m_themeEnumAction);
121 menu->addAction(m_themeAction);
122
123 m_button->setMenu(menu);
124 m_button->setText(tr("..."));
125
126 connect(m_button, &QAbstractButton::clicked, this, &PixmapEditor::defaultActionActivated);
127 connect(m_resourceAction, &QAction::triggered, this, &PixmapEditor::resourceActionActivated);
128 connect(m_fileAction, &QAction::triggered, this, &PixmapEditor::fileActionActivated);
129 connect(m_themeEnumAction, &QAction::triggered, this, &PixmapEditor::themeEnumActionActivated);
130 connect(m_themeAction, &QAction::triggered, this, &PixmapEditor::themeActionActivated);
131#if QT_CONFIG(clipboard)
132 connect(m_copyAction, &QAction::triggered, this, &PixmapEditor::copyActionActivated);
133 connect(m_pasteAction, &QAction::triggered, this, &PixmapEditor::pasteActionActivated);
134#endif
135 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored));
136 setFocusProxy(m_button);
137
138#if QT_CONFIG(clipboard)
139 connect(QApplication::clipboard(), &QClipboard::dataChanged,
140 this, &PixmapEditor::clipboardDataChanged);
141 clipboardDataChanged();
142#endif
143}
144
145void PixmapEditor::setPixmapCache(DesignerPixmapCache *cache)
146{
147 m_pixmapCache = cache;
148}
149
151{
152 if (m_iconThemeModeEnabled == enabled)
153 return;
154 m_iconThemeModeEnabled = enabled;
155 m_themeAction->setVisible(enabled);
156 m_themeEnumAction->setVisible(enabled);
157}
158
159void PixmapEditor::setSpacing(int spacing)
160{
161 m_layout->setSpacing(spacing);
162}
163
164void PixmapEditor::setPath(const QString &path)
165{
166 m_path = path;
167 updateLabels();
168}
169
170void PixmapEditor::setTheme(const QString &theme)
171{
172 m_theme = theme;
173 updateLabels();
174}
175
176QString PixmapEditor::msgThemeIcon(const QString &t)
177{
178 return tr("[Theme] %1").arg(t);
179}
180
181QString PixmapEditor::msgMissingThemeIcon(const QString &t)
182{
183 return tr("[Theme] %1 (missing)").arg(t);
184}
185
187{
188 m_themeEnum = e;
189 updateLabels();
190}
191
192void PixmapEditor::updateLabels()
193{
194 m_pathLabel->setText(displayText(m_themeEnum, m_theme, m_path));
195 switch (state()) {
196 case State::Empty:
197 case State::MissingXdgTheme:
198 case State::MissingThemeEnum:
199 m_pixmapLabel->setPixmap(m_defaultPixmap);
200 m_copyAction->setEnabled(false);
201 break;
202 case State::ThemeEnum:
203 m_pixmapLabel->setPixmap(QIcon::fromTheme(static_cast<QIcon::ThemeIcon>(m_themeEnum)).pixmap(ICON_SIZE));
204 m_copyAction->setEnabled(true);
205 break;
206 case State::XdgTheme:
207 m_pixmapLabel->setPixmap(QIcon::fromTheme(m_theme).pixmap(ICON_SIZE));
208 m_copyAction->setEnabled(true);
209 break;
210 case State::Path:
211 case State::PathFallback:
212 if (m_pixmapCache) {
213 auto pixmap = m_pixmapCache->pixmap(PropertySheetPixmapValue(m_path));
214 m_pixmapLabel->setPixmap(QIcon(pixmap).pixmap(ICON_SIZE));
215 }
216 m_copyAction->setEnabled(true);
217 break;
218 }
219}
220
221void PixmapEditor::setDefaultPixmapIcon(const QIcon &icon)
222{
223 m_defaultPixmap = icon.pixmap(ICON_SIZE);
224 if (state() == State::Empty)
225 m_pixmapLabel->setPixmap(m_defaultPixmap);
226}
227
228void PixmapEditor::setDefaultPixmap(const QPixmap &pixmap)
229{
230 setDefaultPixmapIcon(QIcon(pixmap));
231}
232
233void PixmapEditor::contextMenuEvent(QContextMenuEvent *event)
234{
235 QMenu menu(this);
236 menu.addAction(m_copyAction);
237 menu.addAction(m_pasteAction);
238 menu.exec(event->globalPos());
239 event->accept();
240}
241
242void PixmapEditor::defaultActionActivated()
243{
244 if (m_iconThemeModeEnabled) {
245 themeEnumActionActivated();
246 return;
247 }
248 // Default to resource
249 const PropertySheetPixmapValue::PixmapSource ps = m_path.isEmpty()
250 ? PropertySheetPixmapValue::ResourcePixmap
251 : PropertySheetPixmapValue::getPixmapSource(m_core, m_path);
252 switch (ps) {
253 case PropertySheetPixmapValue::LanguageResourcePixmap:
254 case PropertySheetPixmapValue::ResourcePixmap:
255 resourceActionActivated();
256 break;
257 case PropertySheetPixmapValue::FilePixmap:
258 fileActionActivated();
259 break;
260 }
261}
262
263void PixmapEditor::resourceActionActivated()
264{
265 const QString oldPath = m_path;
266 const QString newPath = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(),
267 oldPath, this);
268 if (!newPath.isEmpty() && newPath != oldPath) {
269 setTheme({});
271 setPath(newPath);
272 emit pathChanged(newPath);
273 }
274}
275
276void PixmapEditor::fileActionActivated()
277{
278 const QString newPath = IconSelector::choosePixmapFile(m_path, m_core->dialogGui(), this);
279 if (!newPath.isEmpty() && newPath != m_path) {
280 setTheme({});
282 setPath(newPath);
283 emit pathChanged(newPath);
284 }
285}
286
287void PixmapEditor::themeEnumActionActivated()
288{
289 const auto newThemeO = IconThemeEnumDialog::getTheme(this, {});
290 if (newThemeO.has_value()) {
291 const int newTheme = newThemeO.value();
292 if (newTheme != m_themeEnum) {
293 setThemeEnum(newTheme);
294 setTheme({});
295 setPath({});
296 emit themeEnumChanged(newTheme);
297 }
298 }
299}
300
301void PixmapEditor::themeActionActivated()
302{
303 const auto newThemeO = IconThemeDialog::getTheme(this, m_theme);
304 if (newThemeO.has_value()) {
305 const QString newTheme = newThemeO.value();
306 if (newTheme != m_theme) {
307 setTheme(newTheme);
309 setPath({});
310 emit themeChanged(newTheme);
311 }
312 }
313}
314
315PixmapEditor::State PixmapEditor::stateFromData(int themeEnum, const QString &xdgTheme,
316 const QString &path)
317{
318 if (themeEnum != -1) {
319 if (QIcon::hasThemeIcon(static_cast<QIcon::ThemeIcon>(themeEnum)))
320 return State::ThemeEnum;
321 return path.isEmpty() ? State::MissingThemeEnum : State::PathFallback;
322 }
323 if (!xdgTheme.isEmpty()) {
324 if (QIcon::hasThemeIcon(xdgTheme))
325 return State::XdgTheme;
326 return path.isEmpty() ? State::MissingXdgTheme : State::PathFallback;
327 }
328 return path.isEmpty() ? State::Empty : State::Path;
329}
330
331PixmapEditor::State PixmapEditor::state() const
332{
333 return stateFromData(m_themeEnum, m_theme, m_path);
334}
335
336QString PixmapEditor::displayText(int themeEnum, const QString &xdgTheme, const QString &path)
337{
338 switch (stateFromData(themeEnum, xdgTheme, path)) {
339 case State::ThemeEnum:
340 return msgThemeIcon(IconThemeEnumEditor::iconName(themeEnum));
341 case State::MissingThemeEnum:
342 return msgMissingThemeIcon(IconThemeEnumEditor::iconName(themeEnum));
343 case State::XdgTheme:
344 return msgThemeIcon(xdgTheme);
345 case State::MissingXdgTheme:
346 return msgMissingThemeIcon(xdgTheme);
347 case State::Path:
348 return QFileInfo(path).fileName();
349 case State::PathFallback:
350 return tr("%1 (fallback)").arg(QFileInfo(path).fileName());
351 case State::Empty:
352 break;
353 }
354 return {};
355}
356
357QString PixmapEditor::displayText(const PropertySheetIconValue &icon)
358{
359 const auto &paths = icon.paths();
360 const auto &it = paths.constFind({QIcon::Normal, QIcon::Off});
361 const QString path = it != paths.constEnd() ? it.value().path() : QString{};
362 return displayText(icon.themeEnum(), icon.theme(), path);
363}
364
365#if QT_CONFIG(clipboard)
367{
369 switch (state()) {
370 case State::ThemeEnum:
373 break;
374 case State::XdgTheme:
377 break;
378 case State::Path:
379 case State::PathFallback:
381 break;
382 case State::Empty:
383 break;
384 }
385}
386
388{
390 QString subtype = u"plain"_s;
392 if (!text.isNull()) {
393 QStringList list = text.split(u'\n');
394 if (!list.isEmpty()) {
395 text = list.at(0);
397 setTheme(text);
398 setPath(QString());
400 } else {
401 setPath(text);
402 setTheme(QString());
404 }
405 }
406 }
407}
408
410{
412 QString subtype = u"plain"_s;
415}
416#endif // QT_CONFIG(clipboard)
417
418} // qdesigner_internal
419
420QT_END_NAMESPACE
void setTheme(const QString &theme)
void setDefaultPixmap(const QPixmap &pixmap)
void setPixmapCache(DesignerPixmapCache *cache)
void setDefaultPixmapIcon(const QIcon &icon)
void setIconThemeModeEnabled(bool enabled)
void contextMenuEvent(QContextMenuEvent *event) override
This event handler, for event event, can be reimplemented in a subclass to receive widget context men...
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static void createIconThemeDialog(QDialog *topLevel, const QString &labelText, QWidget *themeEditor)
static constexpr QSize ICON_SIZE