mainwindow.cpp Example File

widgets/icons/mainwindow.cpp

  /****************************************************************************
  **
  ** Copyright (C) 2016 The Qt Company Ltd.
  ** Contact: https://www.qt.io/licensing/
  **
  ** This file is part of the examples of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:BSD$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see https://www.qt.io/terms-conditions. For further
  ** information use the contact form at https://www.qt.io/contact-us.
  **
  ** BSD License Usage
  ** Alternatively, you may use this file under the terms of the BSD license
  ** as follows:
  **
  ** "Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions are
  ** met:
  **   * Redistributions of source code must retain the above copyright
  **     notice, this list of conditions and the following disclaimer.
  **   * Redistributions in binary form must reproduce the above copyright
  **     notice, this list of conditions and the following disclaimer in
  **     the documentation and/or other materials provided with the
  **     distribution.
  **   * Neither the name of The Qt Company Ltd nor the names of its
  **     contributors may be used to endorse or promote products derived
  **     from this software without specific prior written permission.
  **
  **
  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/

  #include <QtWidgets>

  #include "iconpreviewarea.h"
  #include "iconsizespinbox.h"
  #include "imagedelegate.h"
  #include "mainwindow.h"

  enum { OtherSize = QStyle::PM_CustomBase };

  MainWindow::MainWindow()
  {
      QWidget *centralWidget = new QWidget(this);
      setCentralWidget(centralWidget);

      createActions();

      QGridLayout *mainLayout = new QGridLayout(centralWidget);

      QGroupBox *previewGroupBox = new QGroupBox(tr("Preview"));
      previewArea = new IconPreviewArea(previewGroupBox);
      QVBoxLayout *previewLayout = new QVBoxLayout(previewGroupBox);
      previewLayout->addWidget(previewArea);

      mainLayout->addWidget(previewGroupBox, 0, 0, 1, 2);
      mainLayout->addWidget(createImagesGroupBox(), 1, 0);
      QVBoxLayout *vBox = new QVBoxLayout;
      vBox->addWidget(createIconSizeGroupBox());
      vBox->addWidget(createHighDpiIconSizeGroupBox());
      vBox->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
      mainLayout->addLayout(vBox, 1, 1);
      createContextMenu();

      setWindowTitle(tr("Icons"));
      checkCurrentStyle();
      sizeButtonGroup->button(OtherSize)->click();
  }

  void MainWindow::show()
  {
      QMainWindow::show();
      connect(windowHandle(), &QWindow::screenChanged, this, &MainWindow::screenChanged);
      screenChanged();
  }

  void MainWindow::about()
  {
      QMessageBox::about(this, tr("About Icons"),
              tr("The <b>Icons</b> example illustrates how Qt renders an icon in "
                 "different modes (active, normal, disabled, and selected) and "
                 "states (on and off) based on a set of images."));
  }

  void MainWindow::changeStyle(bool checked)
  {
      if (!checked)
          return;

      const QAction *action = qobject_cast<QAction *>(sender());
      QStyle *style = QStyleFactory::create(action->data().toString());
      Q_ASSERT(style);
      QApplication::setStyle(style);

      foreach (QAbstractButton *button, sizeButtonGroup->buttons()) {
          const QStyle::PixelMetric metric = static_cast<QStyle::PixelMetric>(sizeButtonGroup->id(button));
          const int value = style->pixelMetric(metric);
          switch (metric) {
          case QStyle::PM_SmallIconSize:
              button->setText(tr("Small (%1 x %1)").arg(value));
              break;
          case QStyle::PM_LargeIconSize:
              button->setText(tr("Large (%1 x %1)").arg(value));
              break;
          case QStyle::PM_ToolBarIconSize:
              button->setText(tr("Toolbars (%1 x %1)").arg(value));
              break;
          case QStyle::PM_ListViewIconSize:
              button->setText(tr("List views (%1 x %1)").arg(value));
              break;
          case QStyle::PM_IconViewIconSize:
              button->setText(tr("Icon views (%1 x %1)").arg(value));
              break;
          case QStyle::PM_TabBarIconSize:
              button->setText(tr("Tab bars (%1 x %1)").arg(value));
              break;
          default:
              break;
          }
      }

      triggerChangeSize();
  }

  void MainWindow::changeSize(int id, bool checked)
  {
      if (!checked)
          return;

      const bool other = id == int(OtherSize);
      const int extent = other
          ? otherSpinBox->value()
          : QApplication::style()->pixelMetric(static_cast<QStyle::PixelMetric>(id));

      previewArea->setSize(QSize(extent, extent));
      otherSpinBox->setEnabled(other);
  }

  void MainWindow::triggerChangeSize()
  {
      changeSize(sizeButtonGroup->checkedId(), true);
  }

  void MainWindow::changeIcon()
  {
      QIcon icon;

      for (int row = 0; row < imagesTable->rowCount(); ++row) {
          const QTableWidgetItem *fileItem = imagesTable->item(row, 0);
          const QTableWidgetItem *modeItem = imagesTable->item(row, 1);
          const QTableWidgetItem *stateItem = imagesTable->item(row, 2);

          if (fileItem->checkState() == Qt::Checked) {
              const int modeIndex = IconPreviewArea::iconModeNames().indexOf(modeItem->text());
              Q_ASSERT(modeIndex >= 0);
              const int stateIndex = IconPreviewArea::iconStateNames().indexOf(stateItem->text());
              Q_ASSERT(stateIndex >= 0);
              const QIcon::Mode mode = IconPreviewArea::iconModes().at(modeIndex);
              const QIcon::State state = IconPreviewArea::iconStates().at(stateIndex);

              const QString fileName = fileItem->data(Qt::UserRole).toString();
              QImage image(fileName);
              if (!image.isNull())
                  icon.addPixmap(QPixmap::fromImage(image), mode, state);
          }
      }

      previewArea->setIcon(icon);
  }

  void MainWindow::addSampleImages()
  {
      addImages(QLatin1String(SRCDIR) + QLatin1String("/images"));
  }

  void MainWindow::addOtherImages()
  {
      static bool firstInvocation = true;
      QString directory;
      if (firstInvocation) {
          firstInvocation = false;
          directory = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).value(0, QString());
      }
      addImages(directory);
  }

  void MainWindow::addImages(const QString &directory)
  {
      QFileDialog fileDialog(this, tr("Open Images"), directory);
      QStringList mimeTypeFilters;
      foreach (const QByteArray &mimeTypeName, QImageReader::supportedMimeTypes())
          mimeTypeFilters.append(mimeTypeName);
      mimeTypeFilters.sort();
      fileDialog.setMimeTypeFilters(mimeTypeFilters);
      fileDialog.selectMimeTypeFilter(QLatin1String("image/png"));
      fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
      fileDialog.setFileMode(QFileDialog::ExistingFiles);
      if (!nativeFileDialogAct->isChecked())
          fileDialog.setOption(QFileDialog::DontUseNativeDialog);
      if (fileDialog.exec() == QDialog::Accepted)
          loadImages(fileDialog.selectedFiles());
  }

  void MainWindow::loadImages(const QStringList &fileNames)
  {
      foreach (const QString &fileName, fileNames) {
          const int row = imagesTable->rowCount();
          imagesTable->setRowCount(row + 1);
          const QFileInfo fileInfo(fileName);
          const QString imageName = fileInfo.baseName();
          const QString fileName2x = fileInfo.absolutePath()
              + QLatin1Char('/') + imageName + QLatin1String("@2x.") + fileInfo.suffix();
          const QFileInfo fileInfo2x(fileName2x);
          const QImage image(fileName);
          const QString toolTip =
              tr("Directory: %1\nFile: %2\nFile@2x: %3\nSize: %4x%5")
                 .arg(QDir::toNativeSeparators(fileInfo.absolutePath()), fileInfo.fileName())
                 .arg(fileInfo2x.exists() ? fileInfo2x.fileName() : tr("<None>"))
                 .arg(image.width()).arg(image.height());
          QTableWidgetItem *fileItem = new QTableWidgetItem(imageName);
          fileItem->setData(Qt::UserRole, fileName);
          fileItem->setIcon(QPixmap::fromImage(image));
          fileItem->setFlags((fileItem->flags() | Qt::ItemIsUserCheckable) & ~Qt::ItemIsEditable);
          fileItem->setToolTip(toolTip);

          QIcon::Mode mode = QIcon::Normal;
          QIcon::State state = QIcon::Off;
          if (guessModeStateAct->isChecked()) {
              if (imageName.contains(QLatin1String("_act"), Qt::CaseInsensitive))
                  mode = QIcon::Active;
              else if (imageName.contains(QLatin1String("_dis"), Qt::CaseInsensitive))
                  mode = QIcon::Disabled;
              else if (imageName.contains(QLatin1String("_sel"), Qt::CaseInsensitive))
                  mode = QIcon::Selected;

              if (imageName.contains(QLatin1String("_on"), Qt::CaseInsensitive))
                  state = QIcon::On;
          }

          imagesTable->setItem(row, 0, fileItem);
          QTableWidgetItem *modeItem =
              new QTableWidgetItem(IconPreviewArea::iconModeNames().at(IconPreviewArea::iconModes().indexOf(mode)));
          modeItem->setToolTip(toolTip);
          imagesTable->setItem(row, 1, modeItem);
          QTableWidgetItem *stateItem =
              new QTableWidgetItem(IconPreviewArea::iconStateNames().at(IconPreviewArea::iconStates().indexOf(state)));
          stateItem->setToolTip(toolTip);
          imagesTable->setItem(row, 2, stateItem);
          imagesTable->openPersistentEditor(modeItem);
          imagesTable->openPersistentEditor(stateItem);

          fileItem->setCheckState(Qt::Checked);
      }
  }

  void MainWindow::useHighDpiPixmapsChanged(int checkState)
  {
      QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, checkState == Qt::Checked);
      changeIcon();
  }

  void MainWindow::removeAllImages()
  {
      imagesTable->setRowCount(0);
      changeIcon();
  }

  QWidget *MainWindow::createImagesGroupBox()
  {
      QGroupBox *imagesGroupBox = new QGroupBox(tr("Images"));

      imagesTable = new QTableWidget;
      imagesTable->setSelectionMode(QAbstractItemView::NoSelection);
      imagesTable->setItemDelegate(new ImageDelegate(this));

      QStringList labels;
      labels << tr("Image") << tr("Mode") << tr("State");

      imagesTable->horizontalHeader()->setDefaultSectionSize(90);
      imagesTable->setColumnCount(3);
      imagesTable->setHorizontalHeaderLabels(labels);
      imagesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
      imagesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
      imagesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
      imagesTable->verticalHeader()->hide();

      connect(imagesTable, &QTableWidget::itemChanged,
              this, &MainWindow::changeIcon);

      QVBoxLayout *layout = new QVBoxLayout(imagesGroupBox);
      layout->addWidget(imagesTable);
      return imagesGroupBox;
  }

  QWidget *MainWindow::createIconSizeGroupBox()
  {
      QGroupBox *iconSizeGroupBox = new QGroupBox(tr("Icon Size"));

      sizeButtonGroup = new QButtonGroup(this);
      sizeButtonGroup->setExclusive(true);

      connect(sizeButtonGroup, QOverload<int, bool>::of(&QButtonGroup::buttonToggled),
              this, &MainWindow::changeSize);

      QRadioButton *smallRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(smallRadioButton, QStyle::PM_SmallIconSize);
      QRadioButton *largeRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(largeRadioButton, QStyle::PM_LargeIconSize);
      QRadioButton *toolBarRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(toolBarRadioButton, QStyle::PM_ToolBarIconSize);
      QRadioButton *listViewRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(listViewRadioButton, QStyle::PM_ListViewIconSize);
      QRadioButton *iconViewRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(iconViewRadioButton, QStyle::PM_IconViewIconSize);
      QRadioButton *tabBarRadioButton = new QRadioButton;
      sizeButtonGroup->addButton(tabBarRadioButton, QStyle::PM_TabBarIconSize);
      QRadioButton *otherRadioButton = new QRadioButton(tr("Other:"));
      sizeButtonGroup->addButton(otherRadioButton, OtherSize);
      otherSpinBox = new IconSizeSpinBox;
      otherSpinBox->setRange(8, 256);
      const QString spinBoxToolTip =
          tr("Enter a custom size within %1..%2")
             .arg(otherSpinBox->minimum()).arg(otherSpinBox->maximum());
      otherSpinBox->setValue(64);
      otherSpinBox->setToolTip(spinBoxToolTip);
      otherRadioButton->setToolTip(spinBoxToolTip);

      connect(otherSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
              this, &MainWindow::triggerChangeSize);

      QHBoxLayout *otherSizeLayout = new QHBoxLayout;
      otherSizeLayout->addWidget(otherRadioButton);
      otherSizeLayout->addWidget(otherSpinBox);
      otherSizeLayout->addStretch();

      QGridLayout *layout = new QGridLayout(iconSizeGroupBox);
      layout->addWidget(smallRadioButton, 0, 0);
      layout->addWidget(largeRadioButton, 1, 0);
      layout->addWidget(toolBarRadioButton, 2, 0);
      layout->addWidget(listViewRadioButton, 0, 1);
      layout->addWidget(iconViewRadioButton, 1, 1);
      layout->addWidget(tabBarRadioButton, 2, 1);
      layout->addLayout(otherSizeLayout, 3, 0, 1, 2);
      layout->setRowStretch(4, 1);
      return iconSizeGroupBox;
  }

  void MainWindow::screenChanged()
  {
      devicePixelRatioLabel->setText(QString::number(devicePixelRatioF()));
      if (const QWindow *window = windowHandle()) {
          const QScreen *screen = window->screen();
          const QString screenDescription =
              tr("\"%1\" (%2x%3)").arg(screen->name())
                 .arg(screen->geometry().width()).arg(screen->geometry().height());
          screenNameLabel->setText(screenDescription);
      }
      changeIcon();
  }

  QWidget *MainWindow::createHighDpiIconSizeGroupBox()
  {
      QGroupBox *highDpiGroupBox = new QGroupBox(tr("High DPI Scaling"));
      QFormLayout *layout = new QFormLayout(highDpiGroupBox);
      devicePixelRatioLabel = new QLabel(highDpiGroupBox);
      screenNameLabel = new QLabel(highDpiGroupBox);
      layout->addRow(tr("Screen:"), screenNameLabel);
      layout->addRow(tr("Device pixel ratio:"), devicePixelRatioLabel);
      QCheckBox *highDpiPixmapsCheckBox = new QCheckBox(QLatin1String("Qt::AA_UseHighDpiPixmaps"));
      highDpiPixmapsCheckBox->setChecked(QCoreApplication::testAttribute(Qt::AA_UseHighDpiPixmaps));
      connect(highDpiPixmapsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::useHighDpiPixmapsChanged);
      layout->addRow(highDpiPixmapsCheckBox);
      return highDpiGroupBox;
  }

  void MainWindow::createActions()
  {
      QMenu *fileMenu = menuBar()->addMenu(tr("&File"));

      addSampleImagesAct = new QAction(tr("Add &Sample Images..."), this);
      addSampleImagesAct->setShortcut(tr("Ctrl+A"));
      connect(addSampleImagesAct, &QAction::triggered, this, &MainWindow::addSampleImages);
      fileMenu->addAction(addSampleImagesAct);

      addOtherImagesAct = new QAction(tr("&Add Images..."), this);
      addOtherImagesAct->setShortcut(QKeySequence::Open);
      connect(addOtherImagesAct, &QAction::triggered, this, &MainWindow::addOtherImages);
      fileMenu->addAction(addOtherImagesAct);

      removeAllImagesAct = new QAction(tr("&Remove All Images"), this);
      removeAllImagesAct->setShortcut(tr("Ctrl+R"));
      connect(removeAllImagesAct, &QAction::triggered,
              this, &MainWindow::removeAllImages);
      fileMenu->addAction(removeAllImagesAct);

      fileMenu->addSeparator();

      QAction *exitAct = fileMenu->addAction(tr("&Quit"), this, &QWidget::close);
      exitAct->setShortcuts(QKeySequence::Quit);

      QMenu *viewMenu = menuBar()->addMenu(tr("&View"));

      styleActionGroup = new QActionGroup(this);
      foreach (const QString &styleName, QStyleFactory::keys()) {
          QAction *action = new QAction(tr("%1 Style").arg(styleName), styleActionGroup);
          action->setData(styleName);
          action->setCheckable(true);
          connect(action, &QAction::triggered, this, &MainWindow::changeStyle);
          viewMenu->addAction(action);
      }

      QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));

      guessModeStateAct = new QAction(tr("&Guess Image Mode/State"), this);
      guessModeStateAct->setCheckable(true);
      guessModeStateAct->setChecked(true);
      settingsMenu->addAction(guessModeStateAct);

      nativeFileDialogAct = new QAction(tr("&Use Native File Dialog"), this);
      nativeFileDialogAct->setCheckable(true);
      nativeFileDialogAct->setChecked(true);
      settingsMenu->addAction(nativeFileDialogAct);

      QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
      helpMenu->addAction(tr("&About"), this, &MainWindow::about);
      helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
  }

  void MainWindow::createContextMenu()
  {
      imagesTable->setContextMenuPolicy(Qt::ActionsContextMenu);
      imagesTable->addAction(addSampleImagesAct);
      imagesTable->addAction(addOtherImagesAct);
      imagesTable->addAction(removeAllImagesAct);
  }

  void MainWindow::checkCurrentStyle()
  {
      foreach (QAction *action, styleActionGroup->actions()) {
          QString styleName = action->data().toString();
          QScopedPointer<QStyle> candidate(QStyleFactory::create(styleName));
          Q_ASSERT(!candidate.isNull());
          if (candidate->metaObject()->className()
                  == QApplication::style()->metaObject()->className()) {
              action->trigger();
              return;
          }
      }
  }