From 64896a94368aa88dafe098f7a69e10e8a3a66317 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Tue, 9 Jun 2020 16:25:13 +0300 Subject: [PATCH] Android: parse image file names from manifest Also scales splash images without stretching and reflects them to other orientations. Task-number: QTCREATORBUG-24149 Task-number: QTCREATORBUG-24150 Change-Id: Ie23a8ba5f75132971673d3232807e941dfd50f97 Reviewed-by: Alessandro Portale --- ...droidmanifesteditoriconcontainerwidget.cpp | 41 ++- ...androidmanifesteditoriconcontainerwidget.h | 6 +- .../androidmanifesteditoriconwidget.cpp | 116 ++++++-- .../android/androidmanifesteditoriconwidget.h | 19 +- .../android/androidmanifesteditorwidget.cpp | 44 ++- .../android/splashiconcontainerwidget.cpp | 274 ++++++++++++++---- .../android/splashiconcontainerwidget.h | 22 +- 7 files changed, 411 insertions(+), 111 deletions(-) diff --git a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp index a79caa91026..46c7ccc6242 100644 --- a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp @@ -35,9 +35,10 @@ namespace Android { namespace Internal { namespace { -const QString highDpiIconPath = "/res/drawable-hdpi/icon.png"; -const QString mediumDpiIconPath = "/res/drawable-mdpi/icon.png"; -const QString lowDpiIconPath = "/res/drawable-ldpi/icon.png"; +const QString highDpiIconPath = QLatin1String("/res/drawable-hdpi/"); +const QString mediumDpiIconPath = QLatin1String("/res/drawable-mdpi/"); +const QString lowDpiIconPath = QLatin1String("/res/drawable-ldpi/"); +const QString imageSuffix = QLatin1String(".png"); const QSize lowDpiIconSize{32, 32}; const QSize mediumDpiIconSize{48, 48}; const QSize highDpiIconSize{72, 72}; @@ -63,11 +64,15 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg iconLayout->addWidget(line); iconLayout->addStretch(1); + QString iconFileName = m_iconFileName + imageSuffix; + auto lIconButton = new AndroidManifestEditorIconWidget(this, lowDpiIconSize, lowDpiIconSize, tr("Low DPI icon"), tr("Select low DPI icon"), - textEditorWidget, lowDpiIconPath); + textEditorWidget, + lowDpiIconPath, + iconFileName); iconLayout->addWidget(lIconButton); m_iconButtons.push_back(lIconButton); iconLayout->addStretch(1); @@ -76,7 +81,9 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg mediumDpiIconSize, mediumDpiIconSize, tr("Medium DPI icon"), tr("Select medium DPI icon"), - textEditorWidget, mediumDpiIconPath); + textEditorWidget, + mediumDpiIconPath, + iconFileName); iconLayout->addWidget(mIconButton); m_iconButtons.push_back(mIconButton); iconLayout->addStretch(1); @@ -85,7 +92,9 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg highDpiIconSize, highDpiIconSize, tr("High DPI icon"), tr("Select high DPI icon"), - textEditorWidget, highDpiIconPath); + textEditorWidget, + highDpiIconPath, + iconFileName); iconLayout->addWidget(hIconButton); m_iconButtons.push_back(hIconButton); iconLayout->addStretch(6); @@ -96,13 +105,25 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg } } -void AndroidManifestEditorIconContainerWidget::loadIcons() +void AndroidManifestEditorIconContainerWidget::setIconFileName(const QString &name) { - for (auto &&iconButton : m_iconButtons) - iconButton->loadIcon(); + m_iconFileName = name; } -bool AndroidManifestEditorIconContainerWidget::hasIcons() +QString AndroidManifestEditorIconContainerWidget::iconFileName() const +{ + return m_iconFileName; +} + +void AndroidManifestEditorIconContainerWidget::loadIcons() +{ + for (auto &&iconButton : m_iconButtons) { + iconButton->setTargetIconFileName(m_iconFileName + imageSuffix); + iconButton->loadIcon(); + } +} + +bool AndroidManifestEditorIconContainerWidget::hasIcons() const { for (auto &&iconButton : m_iconButtons) { if (iconButton->hasIcon()) diff --git a/src/plugins/android/androidmanifesteditoriconcontainerwidget.h b/src/plugins/android/androidmanifesteditoriconcontainerwidget.h index b1d0415e9b7..b155dc80af6 100644 --- a/src/plugins/android/androidmanifesteditoriconcontainerwidget.h +++ b/src/plugins/android/androidmanifesteditoriconcontainerwidget.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include @@ -42,10 +43,13 @@ class AndroidManifestEditorIconContainerWidget : public QWidget public: explicit AndroidManifestEditorIconContainerWidget(QWidget *parent, TextEditor::TextEditorWidget *textEditorWidget); + void setIconFileName(const QString &name); + QString iconFileName() const; void loadIcons(); - bool hasIcons(); + bool hasIcons() const; private: QVector m_iconButtons; + QString m_iconFileName = QLatin1String("icon"); }; } // namespace Internal diff --git a/src/plugins/android/androidmanifesteditoriconwidget.cpp b/src/plugins/android/androidmanifesteditoriconwidget.cpp index 5d0407ca759..455c773f615 100644 --- a/src/plugins/android/androidmanifesteditoriconwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconwidget.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -59,9 +60,11 @@ AndroidManifestEditorIconWidget::AndroidManifestEditorIconWidget( QWidget *parent, const QSize &iconSize, const QSize &buttonSize, const QString &title, const QString &tooltip, TextEditor::TextEditorWidget *textEditorWidget, - const QString &targetIconPath) + const QString &targetIconPath, + const QString &targetIconFileName) : QWidget(parent), m_iconSize(iconSize), m_buttonSize(buttonSize), - m_textEditorWidget(textEditorWidget), m_targetIconPath(targetIconPath) + m_textEditorWidget(textEditorWidget), + m_targetIconPath(targetIconPath), m_targetIconFileName(targetIconFileName) { auto iconLayout = new QVBoxLayout(this); auto iconTitle = new QLabel(title, this); @@ -108,12 +111,9 @@ AndroidManifestEditorIconWidget::AndroidManifestEditorIconWidget( this->setLayout(iconLayout); connect(m_button, &QAbstractButton::clicked, this, &AndroidManifestEditorIconWidget::selectIcon); - if (clearButton) { + if (clearButton) connect(clearButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorIconWidget::removeIcon); - connect(clearButton, &QAbstractButton::clicked, - this, &AndroidManifestEditorIconWidget::iconRemoved); - } + this, &AndroidManifestEditorIconWidget::clearIcon); m_iconSelectionText = tooltip; } @@ -122,24 +122,27 @@ void AndroidManifestEditorIconWidget::setIcon(const QIcon &icon) m_button->setIcon(icon); } +void AndroidManifestEditorIconWidget::clearIcon() +{ + removeIcon(); + iconRemoved(); +} + void AndroidManifestEditorIconWidget::loadIcon() { QString baseDir = manifestDir(m_textEditorWidget); - QString iconFile = baseDir + m_targetIconPath; + QString iconFile = baseDir + m_targetIconPath + m_targetIconFileName; setIconFromPath(iconFile); } void AndroidManifestEditorIconWidget::setIconFromPath(const QString &iconPath) { - iconSelected(iconPath); if (!m_textEditorWidget) return; m_iconPath = iconPath; QString baseDir = manifestDir(m_textEditorWidget); - copyIcon(); - QString iconFile = baseDir + m_targetIconPath; - if (!m_scaled) { - QImage original(iconFile); + QImage original(iconPath); + if (!original.isNull() && m_scaledToOriginalAspectRatio) { if ((original.width() > original.height() && m_buttonSize.height() > m_buttonSize.width()) || (original.height() > original.width() && m_buttonSize.width() > m_buttonSize.height())) { auto width = m_buttonSize.height(); @@ -148,9 +151,13 @@ void AndroidManifestEditorIconWidget::setIconFromPath(const QString &iconPath) m_button->setMinimumSize(m_buttonSize); m_button->setMaximumSize(m_buttonSize); m_button->setIconSize(m_buttonSize); + auto targetWidth = m_iconSize.height(); + auto targetHeight = m_iconSize.width(); + m_iconSize = QSize(targetWidth, targetHeight); } - } + copyIcon(); + QString iconFile = baseDir + m_targetIconPath + m_targetIconFileName; m_button->setIcon(QIcon(iconFile)); } @@ -161,12 +168,13 @@ void AndroidManifestEditorIconWidget::selectIcon() if (file.isEmpty()) return; setIconFromPath(file); + iconSelected(file, this); } void AndroidManifestEditorIconWidget::removeIcon() { QString baseDir = manifestDir(m_textEditorWidget); - const QString targetPath = baseDir + m_targetIconPath; + const QString targetPath = baseDir + m_targetIconPath + m_targetIconFileName; if (targetPath.isEmpty()) { qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot remove icon."; return; @@ -178,14 +186,39 @@ void AndroidManifestEditorIconWidget::removeIcon() m_button->setIcon(QIcon()); } -bool AndroidManifestEditorIconWidget::hasIcon() +bool AndroidManifestEditorIconWidget::hasIcon() const { return !m_iconPath.isEmpty(); } -void AndroidManifestEditorIconWidget::setScaled(bool scaled) +void AndroidManifestEditorIconWidget::setScaledToOriginalAspectRatio(bool scaled) { - m_scaled = scaled; + m_scaledToOriginalAspectRatio = scaled; +} + +void AndroidManifestEditorIconWidget::setScaledWithoutStretching(bool scaled) +{ + m_scaledWithoutStretching = scaled; +} + +void AndroidManifestEditorIconWidget::setTargetIconFileName(const QString &targetIconFileName) +{ + m_targetIconFileName = targetIconFileName; +} + +void AndroidManifestEditorIconWidget::setTargetIconPath(const QString &targetIconPath) +{ + m_targetIconPath = targetIconPath; +} + +QString AndroidManifestEditorIconWidget::targetIconFileName() const +{ + return m_targetIconFileName; +} + +QString AndroidManifestEditorIconWidget::targetIconPath() const +{ + return m_targetIconPath; } void AndroidManifestEditorIconWidget::setScaleWarningLabelVisible(bool visible) @@ -194,17 +227,42 @@ void AndroidManifestEditorIconWidget::setScaleWarningLabelVisible(bool visible) m_scaleWarningLabel->setVisible(visible); } +static QImage scaleWithoutStretching(const QImage& original, const QSize& targetSize) +{ + QImage ret(targetSize, QImage::Format_ARGB32); + ret.fill(Qt::white); + if (targetSize.height() > targetSize.width()) { + // portrait target, scale to width and paint in the vertical middle + QImage scaled = original.scaledToWidth(targetSize.width()); + int heightDiffHalf = (targetSize.height() - scaled.height()) / 2; + QPainter painter(&ret); + QRect targetRect(0, heightDiffHalf, targetSize.width(), scaled.height()); + QRect sourceRect(0, 0, scaled.width(), scaled.height()); + painter.drawImage(targetRect, scaled, sourceRect); + } else if (targetSize.width() > targetSize.height()) { + // landscape target, scale to height and paint in the horizontal middle + QImage scaled = original.scaledToHeight(targetSize.height()); + int widthDiffHalf = (targetSize.width() - scaled.width()) / 2; + QPainter painter(&ret); + QRect targetRect(widthDiffHalf, 0, scaled.width(), targetSize.height()); + QRect sourceRect(0, 0, scaled.width(), scaled.height()); + painter.drawImage(targetRect, scaled, sourceRect); + } else + ret = original.scaled(targetSize.width(), targetSize.height(), + Qt::KeepAspectRatio, Qt::SmoothTransformation); + return ret; +} + void AndroidManifestEditorIconWidget::copyIcon() { if (m_targetIconPath.isEmpty()) return; QString baseDir = manifestDir(m_textEditorWidget); - const QString targetPath = baseDir + m_targetIconPath; + const QString targetPath = baseDir + m_targetIconPath + m_targetIconFileName; if (targetPath.isEmpty()) { qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot copy icon."; return; } - QFileInfo targetFile(targetPath); QImage original(m_iconPath); if (m_iconPath != targetPath) removeIcon(); @@ -221,15 +279,15 @@ void AndroidManifestEditorIconWidget::copyIcon() m_iconPath.clear(); return; } - QSize targetSize = m_iconSize; - if (m_scaled) { - QImage scaled = original.scaled(targetSize.width(), targetSize.height(), - Qt::KeepAspectRatio, Qt::SmoothTransformation); - setScaleWarningLabelVisible(scaled.width() > original.width() || scaled.height() > original.height()); - scaled.save(targetPath); - } else - original.save(targetPath); - m_iconPath = m_targetIconPath; + QImage scaled; + if (!m_scaledWithoutStretching) + scaled = original.scaled(m_iconSize.width(), m_iconSize.height(), + Qt::KeepAspectRatio, Qt::SmoothTransformation); + else + scaled = scaleWithoutStretching(original, m_iconSize); + setScaleWarningLabelVisible(scaled.width() > original.width() || scaled.height() > original.height()); + scaled.save(targetPath); + m_iconPath = targetPath; } else { m_iconPath.clear(); } diff --git a/src/plugins/android/androidmanifesteditoriconwidget.h b/src/plugins/android/androidmanifesteditoriconwidget.h index c1049cff3ef..63b23d3ca0a 100644 --- a/src/plugins/android/androidmanifesteditoriconwidget.h +++ b/src/plugins/android/androidmanifesteditoriconwidget.h @@ -50,14 +50,21 @@ public: const QString &title, const QString &tooltip, TextEditor::TextEditorWidget *textEditorWidget = nullptr, - const QString &targetIconPath = {}); + const QString &targetIconPath = {}, + const QString &targetIconFileName = {}); void setIcon(const QIcon &icon); + void clearIcon(); void loadIcon(); void setIconFromPath(const QString &iconPath); - bool hasIcon(); - void setScaled(bool scaled); + bool hasIcon() const; + void setScaledToOriginalAspectRatio(bool scaled); + void setScaledWithoutStretching(bool scaled); + void setTargetIconFileName(const QString &targetIconFileName); + void setTargetIconPath(const QString &targetIconPath); + QString targetIconFileName() const; + QString targetIconPath() const; signals: - void iconSelected(const QString &path); + void iconSelected(const QString &path, AndroidManifestEditorIconWidget* iconWidget); void iconRemoved(); private: @@ -73,8 +80,10 @@ private: TextEditor::TextEditorWidget *m_textEditorWidget = nullptr; QString m_iconPath; QString m_targetIconPath; + QString m_targetIconFileName; QString m_iconSelectionText; - bool m_scaled = true; + bool m_scaledToOriginalAspectRatio = false; + bool m_scaledWithoutStretching = false; }; } // namespace Internal diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index de38f3200a0..deb940064c4 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -806,9 +806,20 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) QDomElement activityElem = applicationElement.firstChildElement(QLatin1String("activity")); m_activityNameLineEdit->setText(activityElem.attribute(QLatin1String("android:label"))); + QString appIconValue = applicationElement.attribute(QLatin1String("android:icon")); + if (!appIconValue.isEmpty()) { + QLatin1String drawable = QLatin1String("@drawable/"); + if (appIconValue.startsWith(drawable)) { + QString appIconName = appIconValue.mid(drawable.size()); + m_iconButtons->setIconFileName(appIconName); + } + } + QDomElement metadataElem = activityElem.firstChildElement(QLatin1String("meta-data")); - enum ActivityParseGuard {none = 0, libName = 1, styleExtract = 2, stickySplash = 4, done = 8}; + enum ActivityParseGuard {none = 0, libName = 1, styleExtract = 2, stickySplash = 4, splashImages = 8, done = 16}; int activityParseGuard = ActivityParseGuard::none; + enum SplashImageParseGuard {splashNone = 0, splash = 1, portraitSplash = 2, landscapeSplash = 4, splashDone = 8}; + int splashParseGuard = SplashImageParseGuard::splashNone; while (!metadataElem.isNull()) { if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.lib_name") && !(activityParseGuard & ActivityParseGuard::libName)) { @@ -826,6 +837,29 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc) QString sticky = metadataElem.attribute(QLatin1String("android:value")); m_splashButtons->setSticky(sticky == QLatin1String("true")); activityParseGuard |= ActivityParseGuard::stickySplash; + } else if (metadataElem.attribute(QLatin1String("android:name")) + .startsWith(QLatin1String("android.app.splash_screen_drawable")) + && !(activityParseGuard & ActivityParseGuard::splashImages) + && !(splashParseGuard & SplashImageParseGuard::splashDone)) { + QString attrName = metadataElem.attribute(QLatin1String("android:name")); + QLatin1String drawable = QLatin1String("@drawable/"); + QString splashImageValue = metadataElem.attribute(QLatin1String("android:resource")); + QString splashImageName; + if (splashImageValue.startsWith(drawable)) { + splashImageName = splashImageValue.mid(drawable.size()); + } + if (attrName == QLatin1String("android.app.splash_screen_drawable")) { + m_splashButtons->setImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::splash; + } else if (attrName == QLatin1String("android.app.splash_screen_drawable_portrait")) { + m_splashButtons->setPortraitImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::portraitSplash; + } else if (attrName == QLatin1String("android.app.splash_screen_drawable_landscape")) { + m_splashButtons->setLandscapeImageFileName(splashImageName); + splashParseGuard |= SplashImageParseGuard::landscapeSplash; + } + if (splashParseGuard & SplashImageParseGuard::splashDone) + activityParseGuard |= ActivityParseGuard::splashImages; } if (activityParseGuard == ActivityParseGuard::done) break; @@ -1066,7 +1100,7 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm bool ensureIconAttribute = m_iconButtons->hasIcons(); if (ensureIconAttribute) { keys << QLatin1String("android:icon"); - values << QLatin1String("@drawable/icon"); + values << (QLatin1String("@drawable/") + m_iconButtons->iconFileName()); } else remove << QLatin1String("android:icon"); @@ -1114,15 +1148,15 @@ void AndroidManifestEditorWidget::parseSplashScreen(QXmlStreamWriter &writer) { if (m_splashButtons->hasImages()) writeMetadataElement("android.app.splash_screen_drawable", - "android:resource", "@drawable/logo", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->imageFileName(), writer); if (m_splashButtons->hasPortraitImages()) writeMetadataElement("android.app.splash_screen_drawable_portrait", - "android:resource", "@drawable/logo_portrait", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->portraitImageFileName(), writer); if (m_splashButtons->hasLandscapeImages()) writeMetadataElement("android.app.splash_screen_drawable_landscape", - "android:resource", "@drawable/logo_landscape", + "android:resource", QLatin1String("@drawable/") + m_splashButtons->landscapeImageFileName(), writer); if (m_splashButtons->isSticky()) writeMetadataElement("android.app.splash_screen_sticky", diff --git a/src/plugins/android/splashiconcontainerwidget.cpp b/src/plugins/android/splashiconcontainerwidget.cpp index 89ef2d77949..0b0f6a3adfb 100644 --- a/src/plugins/android/splashiconcontainerwidget.cpp +++ b/src/plugins/android/splashiconcontainerwidget.cpp @@ -26,25 +26,30 @@ #include "splashiconcontainerwidget.h" #include "androidmanifesteditoriconwidget.h" +#include +#include #include #include +#include #include +#include #include namespace Android { namespace Internal { namespace { -const QString highDpiImagePath = "/res/drawable-hdpi/logo.png"; -const QString mediumDpiImagePath = "/res/drawable-mdpi/logo.png"; -const QString lowDpiImagePath = "/res/drawable-ldpi/logo.png"; -const QString highDpiLandscapeImagePath = "/res/drawable-hdpi/logo_landscape.png"; -const QString mediumDpiLandscapeImagePath = "/res/drawable-mdpi/logo_landscape.png"; -const QString lowDpiLandscapeImagePath = "/res/drawable-ldpi/logo_landscape.png"; -const QString highDpiPortraitImagePath = "/res/drawable-hdpi/logo_portrait.png"; -const QString mediumDpiPortraitImagePath = "/res/drawable-mdpi/logo_portrait.png"; -const QString lowDpiPortraitImagePath = "/res/drawable-ldpi/logo_portrait.png"; +const QString highDpiImagePath = QLatin1String("/res/drawable-hdpi/"); +const QString mediumDpiImagePath = QLatin1String("/res/drawable-mdpi/"); +const QString lowDpiImagePath = QLatin1String("/res/drawable-ldpi/"); +const QString highDpiPortraitImagePath = QLatin1String("/res/drawable-port-hdpi/"); +const QString mediumDpiPortraitImagePath = QLatin1String("/res/drawable-port-mdpi/"); +const QString lowDpiPortraitImagePath = QLatin1String("/res/drawable-port-ldpi/"); +const QString highDpiLandscapeImagePath = QLatin1String("/res/drawable-land-hdpi/"); +const QString mediumDpiLandscapeImagePath = QLatin1String("/res/drawable-land-mdpi/"); +const QString lowDpiLandscapeImagePath = QLatin1String("/res/drawable-land-ldpi/"); +const QString imageSuffix = QLatin1String(".png"); const QSize lowDpiImageSize{200, 320}; const QSize mediumDpiImageSize{320, 480}; const QSize highDpiImageSize{480, 720}; @@ -53,6 +58,36 @@ const QSize mediumDpiLandscapeImageSize{480, 320}; const QSize highDpiLandscapeImageSize{720, 480}; const QSize displaySize{48, 72}; const QSize landscapeDisplaySize{72, 48}; +QString manifestDir(TextEditor::TextEditorWidget *textEditorWidget) +{ + // Get the manifest file's directory from its filepath. + return textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); +} +} + +static AndroidManifestEditorIconWidget * addButtonToPage(QWidget *page, + const QSize &splashSize, const QSize &splashDisplaySize, + const QString &title, const QString &tooltip, + TextEditor::TextEditorWidget *textEditorWidget, + const QString &splashPath, + const QString &splashFileName, + QHBoxLayout *iconLayout, + QVector &buttonContainer, + bool scaledToOriginal, + bool scaledWithoutStretching) +{ + auto splashButton = new AndroidManifestEditorIconWidget(page, + splashSize, + splashDisplaySize, + title, + tooltip, + textEditorWidget, + splashPath, splashFileName); + splashButton->setScaledToOriginalAspectRatio(scaledToOriginal); + splashButton->setScaledWithoutStretching(scaledWithoutStretching); + iconLayout->addWidget(splashButton); + buttonContainer.push_back(splashButton); + return splashButton; } static QWidget *createPage(TextEditor::TextEditorWidget *textEditorWidget, @@ -62,45 +97,58 @@ static QWidget *createPage(TextEditor::TextEditorWidget *textEditorWidget, const QSize &splashSize, const QSize &portraitSplashSize, const QSize &landscapeSplashSize, + const QString &splashSizeTooltip, + const QString &portraitSplashSizeTooltip, + const QString &landscapeSplashSizeTooltip, const QString &splashPath, + const QString &splashFileName, const QString &portraitSplashPath, - const QString &landscapeSplashPath) + const QString &portraitSplashFileName, + const QString &landscapeSplashPath, + const QString &landscapeSplashFileName) { QWidget *page = new QWidget(); auto iconLayout = new QHBoxLayout(page); - auto splashButton = new AndroidManifestEditorIconWidget(page, - splashSize, - landscapeDisplaySize, - SplashIconContainerWidget::tr("Splash screen"), - SplashIconContainerWidget::tr("Select splash screen image"), - textEditorWidget, splashPath); - splashButton->setScaled(false); - iconLayout->addWidget(splashButton); - buttonContainer.push_back(splashButton); - iconLayout->addStretch(1); + auto splashButton= addButtonToPage(page, splashSize, landscapeDisplaySize, + SplashIconContainerWidget::tr("Splash screen"), + SplashIconContainerWidget::tr("Select splash screen image") + + splashSizeTooltip, + textEditorWidget, + splashPath, splashFileName + imageSuffix, + iconLayout, + buttonContainer, + true, false); - auto portraitButton = new AndroidManifestEditorIconWidget(page, - portraitSplashSize, - displaySize, - SplashIconContainerWidget::tr("Portrait splash screen"), - SplashIconContainerWidget::tr("Select portrait splash screen image"), - textEditorWidget, portraitSplashPath); - iconLayout->addWidget(portraitButton); - portraitButtonContainer.push_back(portraitButton); - iconLayout->addStretch(1); + auto portraitButton = addButtonToPage(page, portraitSplashSize, displaySize, + SplashIconContainerWidget::tr("Portrait splash screen"), + SplashIconContainerWidget::tr("Select portrait splash screen image") + + portraitSplashSizeTooltip, + textEditorWidget, + portraitSplashPath, portraitSplashFileName + imageSuffix, + iconLayout, + portraitButtonContainer, + false, true); - auto landscapeButton = new AndroidManifestEditorIconWidget(page, - landscapeSplashSize, - landscapeDisplaySize, - SplashIconContainerWidget::tr("Landscape splash screen"), - SplashIconContainerWidget::tr("Select landscape splash screen image"), - textEditorWidget, landscapeSplashPath); - iconLayout->addWidget(landscapeButton); - landscapeButtonContainer.push_back(landscapeButton); - iconLayout->addStretch(1); - - iconLayout->addStretch(6); + auto landscapeButton = addButtonToPage(page, landscapeSplashSize, landscapeDisplaySize, + SplashIconContainerWidget::tr("Landscape splash screen"), + SplashIconContainerWidget::tr("Select landscape splash screen image") + + landscapeSplashSizeTooltip, + textEditorWidget, + landscapeSplashPath, landscapeSplashFileName + imageSuffix, + iconLayout, + landscapeButtonContainer, + false, true); + auto clearButton = new QToolButton(page); + clearButton->setText(SplashIconContainerWidget::tr("Clear all")); + iconLayout->addWidget(clearButton); + iconLayout->setAlignment(clearButton, Qt::AlignVCenter); + SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, + splashButton, &AndroidManifestEditorIconWidget::clearIcon); + SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, + portraitButton, &AndroidManifestEditorIconWidget::clearIcon); + SplashIconContainerWidget::connect(clearButton, &QAbstractButton::clicked, + landscapeButton, &AndroidManifestEditorIconWidget::clearIcon); return page; } @@ -108,7 +156,7 @@ static QWidget *createPage(TextEditor::TextEditorWidget *textEditorWidget, SplashIconContainerWidget::SplashIconContainerWidget( QWidget *parent, TextEditor::TextEditorWidget *textEditorWidget) - : QWidget(parent) + : QWidget(parent), m_textEditorWidget(textEditorWidget) { auto layout = new QVBoxLayout(this); m_stickyCheck = new QCheckBox(tr("Sticky splash screen"), this); @@ -121,39 +169,55 @@ SplashIconContainerWidget::SplashIconContainerWidget( highDpiImageSize, highDpiImageSize, highDpiLandscapeImageSize, - highDpiImagePath, highDpiPortraitImagePath, highDpiLandscapeImagePath); + tr(" (480x720)"), tr(" (480x720)"), tr(" (720x480)"), + highDpiImagePath, m_imageFileName, + highDpiImagePath, m_portraitImageFileName, + highDpiImagePath, m_landscapeImageFileName); tab->addTab(hdpiPage, tr("High DPI splash screen")); auto mdpiPage = createPage(textEditorWidget, m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, mediumDpiImageSize, mediumDpiImageSize, mediumDpiLandscapeImageSize, - mediumDpiImagePath, mediumDpiPortraitImagePath, mediumDpiLandscapeImagePath); + tr(" (320x480)"), tr(" (320x480)"), tr(" (320x480)"), + mediumDpiImagePath, m_imageFileName, + mediumDpiImagePath, m_portraitImageFileName, + mediumDpiImagePath, m_landscapeImageFileName); tab->addTab(mdpiPage, tr("Medium DPI splash screen")); auto ldpiPage = createPage(textEditorWidget, m_imageButtons, m_portraitImageButtons, m_landscapeImageButtons, lowDpiImageSize, lowDpiImageSize, lowDpiLandscapeImageSize, - lowDpiImagePath, lowDpiPortraitImagePath, lowDpiLandscapeImagePath); + tr(" (200x320)"), tr(" (200x320)"), tr(" (320x200)"), + lowDpiImagePath, m_imageFileName, + lowDpiImagePath, m_portraitImageFileName, + lowDpiImagePath, m_landscapeImageFileName); tab->addTab(ldpiPage, tr("Low DPI splash screen")); layout->addWidget(m_stickyCheck); layout->setAlignment(m_stickyCheck, Qt::AlignLeft); layout->addWidget(tab); - for (auto &&imageButton : m_imageButtons) + for (auto &&imageButton : m_imageButtons) { connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, this, &SplashIconContainerWidget::splashScreensModified); + connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, + this, &SplashIconContainerWidget::imageSelected); + } for (auto &&imageButton : m_portraitImageButtons) { connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, this, &SplashIconContainerWidget::splashScreensModified); connect(imageButton, &AndroidManifestEditorIconWidget::iconRemoved, this, &SplashIconContainerWidget::splashScreensModified); + connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, + this, &SplashIconContainerWidget::imageSelected); } for (auto &&imageButton : m_landscapeImageButtons) { connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, this, &SplashIconContainerWidget::splashScreensModified); connect(imageButton, &AndroidManifestEditorIconWidget::iconRemoved, this, &SplashIconContainerWidget::splashScreensModified); + connect(imageButton, &AndroidManifestEditorIconWidget::iconSelected, + this, &SplashIconContainerWidget::imageSelected); } connect(m_stickyCheck, &QCheckBox::stateChanged, [this](int state) { bool old = m_splashScreenSticky; @@ -163,17 +227,48 @@ SplashIconContainerWidget::SplashIconContainerWidget( }); } -void SplashIconContainerWidget::loadImages() +static void maybeChangeImagePath(AndroidManifestEditorIconWidget *widget, + TextEditor::TextEditorWidget *m_textEditorWidget, + const QString &highDpiAlternativePath, + const QString &mediumDpiAlternativePath, + const QString &lowDpiAlternativePath) { - for (auto &&imageButton : m_imageButtons) - imageButton->loadIcon(); - for (auto &&imageButton : m_portraitImageButtons) - imageButton->loadIcon(); - for (auto &&imageButton : m_landscapeImageButtons) - imageButton->loadIcon(); + QString currentPath = widget->targetIconPath(); + QString alternativePath = currentPath; + if (currentPath == highDpiImagePath) + alternativePath = highDpiAlternativePath; + else if (currentPath == mediumDpiImagePath) + alternativePath = mediumDpiAlternativePath; + else if (currentPath == lowDpiImagePath) + alternativePath = lowDpiAlternativePath; + QString baseDir = manifestDir(m_textEditorWidget); + const QString targetPath = baseDir + alternativePath + widget->targetIconFileName(); + QFileInfo image(targetPath); + if (image.exists()) + widget->setTargetIconPath(alternativePath); } -bool SplashIconContainerWidget::hasImages() +void SplashIconContainerWidget::loadImages() +{ + for (auto &&imageButton : m_imageButtons) { + imageButton->setTargetIconFileName(m_imageFileName + imageSuffix); + imageButton->loadIcon(); + } + for (auto &&imageButton : m_portraitImageButtons) { + imageButton->setTargetIconFileName(m_portraitImageFileName + imageSuffix); + maybeChangeImagePath(imageButton, m_textEditorWidget, + highDpiPortraitImagePath, mediumDpiPortraitImagePath, lowDpiPortraitImagePath); + imageButton->loadIcon(); + } + for (auto &&imageButton : m_landscapeImageButtons) { + imageButton->setTargetIconFileName(m_landscapeImageFileName + imageSuffix); + maybeChangeImagePath(imageButton, m_textEditorWidget, + highDpiLandscapeImagePath, mediumDpiLandscapeImagePath, lowDpiLandscapeImagePath); + imageButton->loadIcon(); + } +} + +bool SplashIconContainerWidget::hasImages() const { for (auto &&iconButton : m_imageButtons) { if (iconButton->hasIcon()) @@ -182,7 +277,7 @@ bool SplashIconContainerWidget::hasImages() return false; } -bool SplashIconContainerWidget::hasPortraitImages() +bool SplashIconContainerWidget::hasPortraitImages() const { for (auto &&iconButton : m_portraitImageButtons) { if (iconButton->hasIcon()) @@ -191,7 +286,7 @@ bool SplashIconContainerWidget::hasPortraitImages() return false; } -bool SplashIconContainerWidget::hasLandscapeImages() +bool SplashIconContainerWidget::hasLandscapeImages() const { for (auto &&iconButton : m_landscapeImageButtons) { if (iconButton->hasIcon()) @@ -200,7 +295,7 @@ bool SplashIconContainerWidget::hasLandscapeImages() return false; } -bool SplashIconContainerWidget::isSticky() +bool SplashIconContainerWidget::isSticky() const { return m_splashScreenSticky; } @@ -211,5 +306,72 @@ void SplashIconContainerWidget::setSticky(bool sticky) m_stickyCheck->setCheckState(m_splashScreenSticky ? Qt::Checked : Qt::Unchecked); } +QString SplashIconContainerWidget::imageFileName() const +{ + return m_imageFileName; +} + +QString SplashIconContainerWidget::landscapeImageFileName() const +{ + return m_landscapeImageFileName; +} + +QString SplashIconContainerWidget::portraitImageFileName() const +{ + return m_portraitImageFileName; +} + +void SplashIconContainerWidget::setImageFileName(const QString &name) +{ + m_imageFileName = name; +} + +void SplashIconContainerWidget::setLandscapeImageFileName(const QString &name) +{ + m_landscapeImageFileName = name; +} + +void SplashIconContainerWidget::setPortraitImageFileName(const QString &name) +{ + m_portraitImageFileName = name; +} + +static void reflectImage(const QString &path, + AndroidManifestEditorIconWidget *iconWidget, + const QVector &source, + QVector *firstTarget, + QVector *secondTarget = nullptr, + Qt::Orientation *imageOrientation = nullptr) +{ + for (int i = 0; i < source.size(); ++i) + if (source[i] == iconWidget) { + if (firstTarget && !(*firstTarget)[i]->hasIcon() + && (!imageOrientation || *imageOrientation == Qt::Horizontal)) + (*firstTarget)[i]->setIconFromPath(path); + if (secondTarget && !(*secondTarget)[i]->hasIcon() + && (!imageOrientation || *imageOrientation == Qt::Vertical)) + (*secondTarget)[i]->setIconFromPath(path); + break; + } + +} + +void SplashIconContainerWidget::imageSelected(const QString &path, AndroidManifestEditorIconWidget *iconWidget) +{ + QImage original(path); + Qt::Orientation orientation = Qt::Horizontal; + if (!original.isNull()) { + if (original.width() > original.height()) { + orientation = Qt::Horizontal; + } else { + orientation = Qt::Vertical; + } + } + reflectImage(path, iconWidget, + m_imageButtons, &m_portraitImageButtons, &m_landscapeImageButtons, &orientation); + reflectImage(path, iconWidget, m_portraitImageButtons, &m_landscapeImageButtons); + reflectImage(path, iconWidget, m_landscapeImageButtons, &m_portraitImageButtons); +} + } // namespace Internal } // namespace Android diff --git a/src/plugins/android/splashiconcontainerwidget.h b/src/plugins/android/splashiconcontainerwidget.h index d7d33e0920f..9c0d6389b6d 100644 --- a/src/plugins/android/splashiconcontainerwidget.h +++ b/src/plugins/android/splashiconcontainerwidget.h @@ -46,21 +46,33 @@ class SplashIconContainerWidget : public QWidget Q_OBJECT public: explicit SplashIconContainerWidget(QWidget *parent, - TextEditor::TextEditorWidget *textEditorWidget); + TextEditor::TextEditorWidget *textEditorWidget); void loadImages(); - bool hasImages(); - bool hasPortraitImages(); - bool hasLandscapeImages(); - bool isSticky(); + bool hasImages() const; + bool hasPortraitImages() const; + bool hasLandscapeImages() const; + bool isSticky() const; void setSticky(bool sticky); + QString imageFileName() const; + QString landscapeImageFileName() const; + QString portraitImageFileName() const; + void setImageFileName(const QString &name); + void setLandscapeImageFileName(const QString &name); + void setPortraitImageFileName(const QString &name); signals: void splashScreensModified(); private: + void imageSelected(const QString &path, AndroidManifestEditorIconWidget *iconWidget); +private: + TextEditor::TextEditorWidget *m_textEditorWidget = nullptr; QVector m_imageButtons; QVector m_portraitImageButtons; QVector m_landscapeImageButtons; bool m_splashScreenSticky = false; QCheckBox *m_stickyCheck = nullptr; + QString m_imageFileName = QLatin1String("logo"); + QString m_landscapeImageFileName = QLatin1String("logo_landscape"); + QString m_portraitImageFileName = QLatin1String("logo_portrait"); }; } // namespace Internal