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 examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "mainwindow.h"
52#include "iconpreviewarea.h"
53#include "iconsizespinbox.h"
54#include "imagedelegate.h"
55
56#include <QApplication>
57#include <QButtonGroup>
58#include <QCheckBox>
59#include <QFileDialog>
60#include <QHeaderView>
61#include <QFormLayout>
62#include <QGridLayout>
63#include <QGroupBox>
64#include <QImageReader>
65#include <QLabel>
66#include <QMenuBar>
67#include <QMessageBox>
68#include <QRadioButton>
69#include <QScreen>
70#include <QStandardPaths>
71#include <QStyleFactory>
72#include <QTableWidget>
73#include <QWindow>
74
75//! [40]
76enum { OtherSize = QStyle::PM_CustomBase };
77//! [40]
78
79//! [0]
80MainWindow::MainWindow(QWidget *parent)
81 : QMainWindow(parent)
82{
83 QWidget *centralWidget = new QWidget(this);
84 setCentralWidget(centralWidget);
85
86 createActions();
87
88 QGridLayout *mainLayout = new QGridLayout(centralWidget);
89
90 QGroupBox *previewGroupBox = new QGroupBox(tr(s: "Preview"));
91 previewArea = new IconPreviewArea(previewGroupBox);
92 QVBoxLayout *previewLayout = new QVBoxLayout(previewGroupBox);
93 previewLayout->addWidget(previewArea);
94
95 mainLayout->addWidget(previewGroupBox, row: 0, column: 0, rowSpan: 1, columnSpan: 2);
96 mainLayout->addWidget(createImagesGroupBox(), row: 1, column: 0);
97 QVBoxLayout *vBox = new QVBoxLayout;
98 vBox->addWidget(createIconSizeGroupBox());
99 vBox->addWidget(createHighDpiIconSizeGroupBox());
100 vBox->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
101 mainLayout->addLayout(vBox, row: 1, column: 1);
102 createContextMenu();
103
104 setWindowTitle(tr(s: "Icons"));
105 checkCurrentStyle();
106 sizeButtonGroup->button(id: OtherSize)->click();
107}
108//! [0]
109
110//! [44]
111void MainWindow::show()
112{
113 QMainWindow::show();
114 connect(sender: windowHandle(), signal: &QWindow::screenChanged, receiver: this, slot: &MainWindow::screenChanged);
115 screenChanged();
116}
117//! [44]
118
119//! [1]
120void MainWindow::about()
121{
122 QMessageBox::about(parent: this, title: tr(s: "About Icons"),
123 text: tr(s: "The <b>Icons</b> example illustrates how Qt renders an icon in "
124 "different modes (active, normal, disabled, and selected) and "
125 "states (on and off) based on a set of images."));
126}
127//! [1]
128
129//! [2]
130void MainWindow::changeStyle(bool checked)
131{
132 if (!checked)
133 return;
134
135 const QAction *action = qobject_cast<QAction *>(object: sender());
136//! [2] //! [3]
137 QStyle *style = QStyleFactory::create(action->data().toString());
138//! [3] //! [4]
139 Q_ASSERT(style);
140 QApplication::setStyle(style);
141
142 const QList<QAbstractButton*> buttons = sizeButtonGroup->buttons();
143 for (QAbstractButton *button : buttons) {
144 const QStyle::PixelMetric metric = static_cast<QStyle::PixelMetric>(sizeButtonGroup->id(button));
145 const int value = style->pixelMetric(metric);
146 switch (metric) {
147 case QStyle::PM_SmallIconSize:
148 button->setText(tr(s: "Small (%1 x %1)").arg(a: value));
149 break;
150 case QStyle::PM_LargeIconSize:
151 button->setText(tr(s: "Large (%1 x %1)").arg(a: value));
152 break;
153 case QStyle::PM_ToolBarIconSize:
154 button->setText(tr(s: "Toolbars (%1 x %1)").arg(a: value));
155 break;
156 case QStyle::PM_ListViewIconSize:
157 button->setText(tr(s: "List views (%1 x %1)").arg(a: value));
158 break;
159 case QStyle::PM_IconViewIconSize:
160 button->setText(tr(s: "Icon views (%1 x %1)").arg(a: value));
161 break;
162 case QStyle::PM_TabBarIconSize:
163 button->setText(tr(s: "Tab bars (%1 x %1)").arg(a: value));
164 break;
165 default:
166 break;
167 }
168 }
169
170 triggerChangeSize();
171}
172//! [4]
173
174//! [5]
175void MainWindow::changeSize(int id, bool checked)
176{
177 if (!checked)
178 return;
179
180 const bool other = id == int(OtherSize);
181 const int extent = other
182 ? otherSpinBox->value()
183 : QApplication::style()->pixelMetric(metric: static_cast<QStyle::PixelMetric>(id));
184
185 previewArea->setSize(QSize(extent, extent));
186 otherSpinBox->setEnabled(other);
187}
188
189void MainWindow::triggerChangeSize()
190{
191 changeSize(id: sizeButtonGroup->checkedId(), checked: true);
192}
193//! [5]
194
195//! [6]
196void MainWindow::changeIcon()
197{
198 QIcon icon;
199
200 for (int row = 0; row < imagesTable->rowCount(); ++row) {
201 const QTableWidgetItem *fileItem = imagesTable->item(row, column: 0);
202 const QTableWidgetItem *modeItem = imagesTable->item(row, column: 1);
203 const QTableWidgetItem *stateItem = imagesTable->item(row, column: 2);
204
205 if (fileItem->checkState() == Qt::Checked) {
206 const int modeIndex = IconPreviewArea::iconModeNames().indexOf(t: modeItem->text());
207 Q_ASSERT(modeIndex >= 0);
208 const int stateIndex = IconPreviewArea::iconStateNames().indexOf(t: stateItem->text());
209 Q_ASSERT(stateIndex >= 0);
210 const QIcon::Mode mode = IconPreviewArea::iconModes().at(i: modeIndex);
211 const QIcon::State state = IconPreviewArea::iconStates().at(i: stateIndex);
212//! [6]
213
214//! [8]
215 const QString fileName = fileItem->data(role: Qt::UserRole).toString();
216 QImage image(fileName);
217 if (!image.isNull())
218 icon.addPixmap(pixmap: QPixmap::fromImage(image), mode, state);
219//! [8]
220 }
221 }
222//! [11]
223 previewArea->setIcon(icon);
224//! [11]
225}
226
227void MainWindow::addSampleImages()
228{
229 addImages(directory: QLatin1String(SRCDIR) + QLatin1String("/images"));
230}
231
232void MainWindow::addOtherImages()
233{
234 static bool firstInvocation = true;
235 QString directory;
236 if (firstInvocation) {
237 firstInvocation = false;
238 directory = QStandardPaths::standardLocations(type: QStandardPaths::PicturesLocation).value(i: 0, defaultValue: QString());
239 }
240 addImages(directory);
241}
242
243//! [12]
244void MainWindow::addImages(const QString &directory)
245{
246 QFileDialog fileDialog(this, tr(s: "Open Images"), directory);
247 QStringList mimeTypeFilters;
248 const QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes();
249 for (const QByteArray &mimeTypeName : mimeTypes)
250 mimeTypeFilters.append(t: mimeTypeName);
251 mimeTypeFilters.sort();
252 fileDialog.setMimeTypeFilters(mimeTypeFilters);
253 fileDialog.selectMimeTypeFilter(filter: QLatin1String("image/png"));
254 fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
255 fileDialog.setFileMode(QFileDialog::ExistingFiles);
256 if (!nativeFileDialogAct->isChecked())
257 fileDialog.setOption(option: QFileDialog::DontUseNativeDialog);
258 if (fileDialog.exec() == QDialog::Accepted)
259 loadImages(fileNames: fileDialog.selectedFiles());
260//! [12]
261}
262
263void MainWindow::loadImages(const QStringList &fileNames)
264{
265 for (const QString &fileName : fileNames) {
266 const int row = imagesTable->rowCount();
267 imagesTable->setRowCount(row + 1);
268//! [13]
269 const QFileInfo fileInfo(fileName);
270 const QString imageName = fileInfo.baseName();
271 const QString fileName2x = fileInfo.absolutePath()
272 + QLatin1Char('/') + imageName + QLatin1String("@2x.") + fileInfo.suffix();
273 const QFileInfo fileInfo2x(fileName2x);
274 const QImage image(fileName);
275 const QString toolTip =
276 tr(s: "Directory: %1\nFile: %2\nFile@2x: %3\nSize: %4x%5")
277 .arg(args: QDir::toNativeSeparators(pathName: fileInfo.absolutePath()), args: fileInfo.fileName())
278 .arg(a: fileInfo2x.exists() ? fileInfo2x.fileName() : tr(s: "<None>"))
279 .arg(a: image.width()).arg(a: image.height());
280 QTableWidgetItem *fileItem = new QTableWidgetItem(imageName);
281 fileItem->setData(role: Qt::UserRole, value: fileName);
282 fileItem->setIcon(QPixmap::fromImage(image));
283 fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable);
284 fileItem->setToolTip(toolTip);
285//! [13]
286
287//! [15]
288 QIcon::Mode mode = QIcon::Normal;
289 QIcon::State state = QIcon::Off;
290 if (guessModeStateAct->isChecked()) {
291 if (imageName.contains(s: QLatin1String("_act"), cs: Qt::CaseInsensitive))
292 mode = QIcon::Active;
293 else if (imageName.contains(s: QLatin1String("_dis"), cs: Qt::CaseInsensitive))
294 mode = QIcon::Disabled;
295 else if (imageName.contains(s: QLatin1String("_sel"), cs: Qt::CaseInsensitive))
296 mode = QIcon::Selected;
297
298 if (imageName.contains(s: QLatin1String("_on"), cs: Qt::CaseInsensitive))
299 state = QIcon::On;
300//! [15]
301 }
302
303//! [18]
304 imagesTable->setItem(row, column: 0, item: fileItem);
305 QTableWidgetItem *modeItem =
306 new QTableWidgetItem(IconPreviewArea::iconModeNames().at(i: IconPreviewArea::iconModes().indexOf(t: mode)));
307 modeItem->setToolTip(toolTip);
308 imagesTable->setItem(row, column: 1, item: modeItem);
309 QTableWidgetItem *stateItem =
310 new QTableWidgetItem(IconPreviewArea::iconStateNames().at(i: IconPreviewArea::iconStates().indexOf(t: state)));
311 stateItem->setToolTip(toolTip);
312 imagesTable->setItem(row, column: 2, item: stateItem);
313 imagesTable->openPersistentEditor(item: modeItem);
314 imagesTable->openPersistentEditor(item: stateItem);
315
316 fileItem->setCheckState(Qt::Checked);
317//! [18]
318 }
319}
320
321void MainWindow::useHighDpiPixmapsChanged(int checkState)
322{
323 QCoreApplication::setAttribute(attribute: Qt::AA_UseHighDpiPixmaps, on: checkState == Qt::Checked);
324 changeIcon();
325}
326
327//! [20]
328void MainWindow::removeAllImages()
329{
330 imagesTable->setRowCount(0);
331 changeIcon();
332}
333//! [20]
334
335//! [21]
336QWidget *MainWindow::createImagesGroupBox()
337{
338 QGroupBox *imagesGroupBox = new QGroupBox(tr(s: "Images"));
339
340 imagesTable = new QTableWidget;
341 imagesTable->setSelectionMode(QAbstractItemView::NoSelection);
342 imagesTable->setItemDelegate(new ImageDelegate(this));
343//! [21]
344
345//! [22]
346 const QStringList labels({tr(s: "Image"), tr(s: "Mode"), tr(s: "State")});
347
348 imagesTable->horizontalHeader()->setDefaultSectionSize(90);
349 imagesTable->setColumnCount(3);
350 imagesTable->setHorizontalHeaderLabels(labels);
351 imagesTable->horizontalHeader()->setSectionResizeMode(logicalIndex: 0, mode: QHeaderView::Stretch);
352 imagesTable->horizontalHeader()->setSectionResizeMode(logicalIndex: 1, mode: QHeaderView::Fixed);
353 imagesTable->horizontalHeader()->setSectionResizeMode(logicalIndex: 2, mode: QHeaderView::Fixed);
354 imagesTable->verticalHeader()->hide();
355//! [22]
356
357//! [24]
358 connect(sender: imagesTable, signal: &QTableWidget::itemChanged,
359 receiver: this, slot: &MainWindow::changeIcon);
360
361 QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox);
362 layout->addWidget(imagesTable);
363 return imagesGroupBox;
364//! [24]
365}
366
367//! [26]
368QWidget *MainWindow::createIconSizeGroupBox()
369{
370 QGroupBox *iconSizeGroupBox = new QGroupBox(tr(s: "Icon Size"));
371
372 sizeButtonGroup = new QButtonGroup(this);
373 sizeButtonGroup->setExclusive(true);
374
375 connect(sender: sizeButtonGroup, signal: QOverload<int, bool>::of(ptr: &QButtonGroup::buttonToggled),
376 receiver: this, slot: &MainWindow::changeSize);
377
378 QRadioButton *smallRadioButton = new QRadioButton;
379 sizeButtonGroup->addButton(smallRadioButton, id: QStyle::PM_SmallIconSize);
380 QRadioButton *largeRadioButton = new QRadioButton;
381 sizeButtonGroup->addButton(largeRadioButton, id: QStyle::PM_LargeIconSize);
382 QRadioButton *toolBarRadioButton = new QRadioButton;
383 sizeButtonGroup->addButton(toolBarRadioButton, id: QStyle::PM_ToolBarIconSize);
384 QRadioButton *listViewRadioButton = new QRadioButton;
385 sizeButtonGroup->addButton(listViewRadioButton, id: QStyle::PM_ListViewIconSize);
386 QRadioButton *iconViewRadioButton = new QRadioButton;
387 sizeButtonGroup->addButton(iconViewRadioButton, id: QStyle::PM_IconViewIconSize);
388 QRadioButton *tabBarRadioButton = new QRadioButton;
389 sizeButtonGroup->addButton(tabBarRadioButton, id: QStyle::PM_TabBarIconSize);
390 QRadioButton *otherRadioButton = new QRadioButton(tr(s: "Other:"));
391 sizeButtonGroup->addButton(otherRadioButton, id: OtherSize);
392 otherSpinBox = new IconSizeSpinBox;
393 otherSpinBox->setRange(min: 8, max: 256);
394 const QString spinBoxToolTip =
395 tr(s: "Enter a custom size within %1..%2")
396 .arg(a: otherSpinBox->minimum()).arg(a: otherSpinBox->maximum());
397 otherSpinBox->setValue(64);
398 otherSpinBox->setToolTip(spinBoxToolTip);
399 otherRadioButton->setToolTip(spinBoxToolTip);
400//! [26]
401
402//! [27]
403 connect(sender: otherSpinBox, signal: QOverload<int>::of(ptr: &QSpinBox::valueChanged),
404 receiver: this, slot: &MainWindow::triggerChangeSize);
405
406 QHBoxLayout *otherSizeLayout = new QHBoxLayout;
407 otherSizeLayout->addWidget(otherRadioButton);
408 otherSizeLayout->addWidget(otherSpinBox);
409 otherSizeLayout->addStretch();
410
411 QGridLayout *layout = new QGridLayout(iconSizeGroupBox);
412 layout->addWidget(smallRadioButton, row: 0, column: 0);
413 layout->addWidget(largeRadioButton, row: 1, column: 0);
414 layout->addWidget(toolBarRadioButton, row: 2, column: 0);
415 layout->addWidget(listViewRadioButton, row: 0, column: 1);
416 layout->addWidget(iconViewRadioButton, row: 1, column: 1);
417 layout->addWidget(tabBarRadioButton, row: 2, column: 1);
418 layout->addLayout(otherSizeLayout, row: 3, column: 0, rowSpan: 1, columnSpan: 2);
419 layout->setRowStretch(row: 4, stretch: 1);
420 return iconSizeGroupBox;
421//! [27]
422}
423
424void MainWindow::screenChanged()
425{
426 devicePixelRatioLabel->setText(QString::number(devicePixelRatioF()));
427 if (const QWindow *window = windowHandle()) {
428 const QScreen *screen = window->screen();
429 const QString screenDescription =
430 tr(s: "\"%1\" (%2x%3)").arg(a: screen->name())
431 .arg(a: screen->geometry().width()).arg(a: screen->geometry().height());
432 screenNameLabel->setText(screenDescription);
433 }
434 changeIcon();
435}
436
437QWidget *MainWindow::createHighDpiIconSizeGroupBox()
438{
439 QGroupBox *highDpiGroupBox = new QGroupBox(tr(s: "High DPI Scaling"));
440 QFormLayout *layout = new QFormLayout(highDpiGroupBox);
441 devicePixelRatioLabel = new QLabel(highDpiGroupBox);
442 screenNameLabel = new QLabel(highDpiGroupBox);
443 layout->addRow(labelText: tr(s: "Screen:"), field: screenNameLabel);
444 layout->addRow(labelText: tr(s: "Device pixel ratio:"), field: devicePixelRatioLabel);
445 QCheckBox *highDpiPixmapsCheckBox = new QCheckBox(QLatin1String("Qt::AA_UseHighDpiPixmaps"));
446 highDpiPixmapsCheckBox->setChecked(QCoreApplication::testAttribute(attribute: Qt::AA_UseHighDpiPixmaps));
447 connect(sender: highDpiPixmapsCheckBox, signal: &QCheckBox::stateChanged, receiver: this, slot: &MainWindow::useHighDpiPixmapsChanged);
448 layout->addRow(widget: highDpiPixmapsCheckBox);
449 return highDpiGroupBox;
450}
451
452//! [28]
453void MainWindow::createActions()
454{
455 QMenu *fileMenu = menuBar()->addMenu(title: tr(s: "&File"));
456
457 addSampleImagesAct = new QAction(tr(s: "Add &Sample Images..."), this);
458 addSampleImagesAct->setShortcut(tr(s: "Ctrl+A"));
459 connect(sender: addSampleImagesAct, signal: &QAction::triggered, receiver: this, slot: &MainWindow::addSampleImages);
460 fileMenu->addAction(action: addSampleImagesAct);
461
462 addOtherImagesAct = new QAction(tr(s: "&Add Images..."), this);
463 addOtherImagesAct->setShortcut(QKeySequence::Open);
464 connect(sender: addOtherImagesAct, signal: &QAction::triggered, receiver: this, slot: &MainWindow::addOtherImages);
465 fileMenu->addAction(action: addOtherImagesAct);
466
467 removeAllImagesAct = new QAction(tr(s: "&Remove All Images"), this);
468 removeAllImagesAct->setShortcut(tr(s: "Ctrl+R"));
469 connect(sender: removeAllImagesAct, signal: &QAction::triggered,
470 receiver: this, slot: &MainWindow::removeAllImages);
471 fileMenu->addAction(action: removeAllImagesAct);
472
473 fileMenu->addSeparator();
474
475 QAction *exitAct = fileMenu->addAction(text: tr(s: "&Quit"), object: this, slot: &QWidget::close);
476 exitAct->setShortcuts(QKeySequence::Quit);
477
478 QMenu *viewMenu = menuBar()->addMenu(title: tr(s: "&View"));
479
480 styleActionGroup = new QActionGroup(this);
481 const QStringList styleKeys = QStyleFactory::keys();
482 for (const QString &styleName : styleKeys) {
483 QAction *action = new QAction(tr(s: "%1 Style").arg(a: styleName), styleActionGroup);
484 action->setData(styleName);
485 action->setCheckable(true);
486 connect(sender: action, signal: &QAction::triggered, receiver: this, slot: &MainWindow::changeStyle);
487 viewMenu->addAction(action);
488 }
489
490 QMenu *settingsMenu = menuBar()->addMenu(title: tr(s: "&Settings"));
491
492 guessModeStateAct = new QAction(tr(s: "&Guess Image Mode/State"), this);
493 guessModeStateAct->setCheckable(true);
494 guessModeStateAct->setChecked(true);
495 settingsMenu->addAction(action: guessModeStateAct);
496
497 nativeFileDialogAct = new QAction(tr(s: "&Use Native File Dialog"), this);
498 nativeFileDialogAct->setCheckable(true);
499 nativeFileDialogAct->setChecked(true);
500 settingsMenu->addAction(action: nativeFileDialogAct);
501
502 QMenu *helpMenu = menuBar()->addMenu(title: tr(s: "&Help"));
503 helpMenu->addAction(text: tr(s: "&About"), object: this, slot: &MainWindow::about);
504 helpMenu->addAction(text: tr(s: "About &Qt"), qApp, slot: &QApplication::aboutQt);
505}
506//! [28]
507
508//! [30]
509void MainWindow::createContextMenu()
510{
511 imagesTable->setContextMenuPolicy(Qt::ActionsContextMenu);
512 imagesTable->addAction(action: addSampleImagesAct);
513 imagesTable->addAction(action: addOtherImagesAct);
514 imagesTable->addAction(action: removeAllImagesAct);
515}
516//! [30]
517
518//! [31]
519void MainWindow::checkCurrentStyle()
520{
521 const QList<QAction *> actions = styleActionGroup->actions();
522 for (QAction *action : actions) {
523 const QString styleName = action->data().toString();
524 const std::unique_ptr<QStyle> candidate{QStyleFactory::create(styleName)};
525 Q_ASSERT(candidate);
526 if (candidate->metaObject()->className()
527 == QApplication::style()->metaObject()->className()) {
528 action->trigger();
529 return;
530 }
531 }
532}
533//! [31]
534

source code of qtbase/examples/widgets/widgets/icons/mainwindow.cpp