Android: make icon removal actually work

Also adjusts icon preview sizes and adds a master icon from which
the other icons are generated.

Task-number: QTCREATORBUG-23283
Change-Id: I21c3d11f9b5d4d815dc6d9ad7c2363b67767d03a
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Ville Voutilainen
2020-02-19 12:02:44 +02:00
parent bc62269304
commit 82dab12acd
2 changed files with 224 additions and 74 deletions

View File

@@ -49,32 +49,39 @@
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/utilsicons.h>
#include <QLineEdit>
#include <QFileInfo>
#include <QDomDocument>
#include <QDir>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QFormLayout>
#include <QComboBox>
#include <QSpinBox>
#include <QDebug>
#include <QToolButton>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
#include <QListView> #include <utils/utilsicons.h>
#include <QPushButton>
#include <QFileDialog>
#include <QTimer>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QDir>
#include <QDomDocument>
#include <QFileDialog>
#include <QFileInfo>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QImage>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QLoggingCategory>
#include <QPushButton>
#include <QScrollArea> #include <QScrollArea>
#include <QSpinBox>
#include <QTimer>
#include <QToolButton>
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
namespace {
static Q_LOGGING_CATEGORY(androidManifestEditorLog, "qtc.android.manifestEditor", QtWarningMsg)
}
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Android; using namespace Android;
using namespace Android::Internal; using namespace Android::Internal;
@@ -237,22 +244,44 @@ void AndroidManifestEditorWidget::initializePage()
createDPIButton(iconLayout, createDPIButton(iconLayout,
applicationGroupBox, applicationGroupBox,
m_lIconButton, m_lIconClearButton, m_masterIconButton, iconSize(LowDPI),
tr("Low DPI icon"), tr("Select low DPI icon.")); tr("Master icon"), tr("Select master icon."));
m_masterIconButton->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon()));
iconLayout->addStretch(1);
QFrame* line = new QFrame();
line->setFrameShape(QFrame::VLine);
line->setFrameShadow(QFrame::Sunken);
iconLayout->addWidget(line);
iconLayout->addStretch(1); iconLayout->addStretch(1);
createDPIButton(iconLayout, createDPIButton(iconLayout,
applicationGroupBox, applicationGroupBox,
m_mIconButton, m_mIconClearButton, m_lIconButton, iconSize(LowDPI),
tr("Medium DPI icon"), tr("Select medium DPI icon.")); tr("Low DPI icon"), tr("Select low DPI icon."),
&m_lIconClearButton,
&m_lIconScaleWarningLabel);
iconLayout->addStretch(1); iconLayout->addStretch(1);
createDPIButton(iconLayout, createDPIButton(iconLayout,
applicationGroupBox, applicationGroupBox,
m_hIconButton, m_hIconClearButton, m_mIconButton, iconSize(MediumDPI),
tr("High DPI icon"), tr("Select high DPI icon.")); tr("Medium DPI icon"), tr("Select medium DPI icon."),
&m_mIconClearButton,
&m_mIconScaleWarningLabel);
iconLayout->addStretch(1);
createDPIButton(iconLayout,
applicationGroupBox,
m_hIconButton, iconSize(HighDPI),
tr("High DPI icon"), tr("Select high DPI icon."),
&m_hIconClearButton,
&m_hIconScaleWarningLabel);
iconLayout->addStretch(6); iconLayout->addStretch(6);
@@ -269,6 +298,8 @@ void AndroidManifestEditorWidget::initializePage()
connect(m_targetLineEdit, &QComboBox::currentTextChanged, connect(m_targetLineEdit, &QComboBox::currentTextChanged,
this, setDirtyFunc); this, setDirtyFunc);
connect(m_masterIconButton, &QAbstractButton::clicked,
this, &AndroidManifestEditorWidget::setMasterIcon);
connect(m_lIconButton, &QAbstractButton::clicked, connect(m_lIconButton, &QAbstractButton::clicked,
this, &AndroidManifestEditorWidget::setLDPIIcon); this, &AndroidManifestEditorWidget::setLDPIIcon);
connect(m_mIconButton, &QAbstractButton::clicked, connect(m_mIconButton, &QAbstractButton::clicked,
@@ -549,10 +580,7 @@ void AndroidManifestEditorWidget::setDirty(bool dirty)
bool AndroidManifestEditorWidget::isModified() const bool AndroidManifestEditorWidget::isModified() const
{ {
return m_dirty return m_dirty;
|| !m_hIconPath.isEmpty()
|| !m_mIconPath.isEmpty()
|| !m_lIconPath.isEmpty();
} }
AndroidManifestEditorWidget::EditorPage AndroidManifestEditorWidget::activePage() const AndroidManifestEditorWidget::EditorPage AndroidManifestEditorWidget::activePage() const
@@ -592,18 +620,10 @@ void AndroidManifestEditorWidget::preSave()
syncToEditor(); syncToEditor();
QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath(); QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
if (!m_lIconPath.isEmpty()) { copyIcon(LowDPI, baseDir, m_lIconPath);
copyIcon(LowDPI, baseDir, m_lIconPath); copyIcon(MediumDPI, baseDir, m_mIconPath);
m_lIconPath.clear(); copyIcon(HighDPI, baseDir, m_hIconPath);
}
if (!m_mIconPath.isEmpty()) {
copyIcon(MediumDPI, baseDir, m_mIconPath);
m_mIconPath.clear();
}
if (!m_hIconPath.isEmpty()) {
copyIcon(HighDPI, baseDir, m_hIconPath);
m_hIconPath.clear();
}
// no need to emit changed() since this is called as part of saving // no need to emit changed() since this is called as part of saving
updateInfoBar(); updateInfoBar();
@@ -795,9 +815,9 @@ void AndroidManifestEditorWidget::syncToWidgets(const QDomDocument &doc)
m_lIconButton->setIcon(icon(baseDir, LowDPI)); m_lIconButton->setIcon(icon(baseDir, LowDPI));
m_mIconButton->setIcon(icon(baseDir, MediumDPI)); m_mIconButton->setIcon(icon(baseDir, MediumDPI));
m_hIconButton->setIcon(icon(baseDir, HighDPI)); m_hIconButton->setIcon(icon(baseDir, HighDPI));
m_lIconPath.clear(); m_lIconPath = baseDir + iconPath(LowDPI);
m_mIconPath.clear(); m_mIconPath = baseDir + iconPath(MediumDPI);
m_hIconPath.clear(); m_hIconPath = baseDir + iconPath(HighDPI);
disconnect(m_defaultPermissonsCheckBox, &QCheckBox::stateChanged, disconnect(m_defaultPermissonsCheckBox, &QCheckBox::stateChanged,
this, &AndroidManifestEditorWidget::defaultPermissionOrFeatureCheckBoxClicked); this, &AndroidManifestEditorWidget::defaultPermissionOrFeatureCheckBoxClicked);
@@ -999,15 +1019,17 @@ void AndroidManifestEditorWidget::parseApplication(QXmlStreamReader &reader, QXm
QXmlStreamAttributes attributes = reader.attributes(); QXmlStreamAttributes attributes = reader.attributes();
QStringList keys = {QLatin1String("android:label")}; QStringList keys = {QLatin1String("android:label")};
QStringList values = {m_appNameLineEdit->text()}; QStringList values = {m_appNameLineEdit->text()};
QStringList remove;
bool ensureIconAttribute = !m_lIconPath.isEmpty() bool ensureIconAttribute = !m_lIconPath.isEmpty()
|| !m_mIconPath.isEmpty() || !m_mIconPath.isEmpty()
|| !m_hIconPath.isEmpty(); || !m_hIconPath.isEmpty();
if (ensureIconAttribute) { if (ensureIconAttribute) {
keys << QLatin1String("android:icon"); keys << QLatin1String("android:icon");
values << QLatin1String("@drawable/icon"); values << QLatin1String("@drawable/icon");
} } else
remove << QLatin1String("android:icon");
QXmlStreamAttributes result = modifyXmlStreamAttributes(attributes, keys, values); QXmlStreamAttributes result = modifyXmlStreamAttributes(attributes, keys, values, remove);
writer.writeAttributes(result); writer.writeAttributes(result);
reader.readNext(); reader.readNext();
@@ -1230,6 +1252,34 @@ QString AndroidManifestEditorWidget::iconPath(IconDPI dpi)
return {}; return {};
} }
QSize AndroidManifestEditorWidget::iconSize(IconDPI dpi)
{
switch (dpi) {
case HighDPI:
return QSize(72, 72);
case MediumDPI:
return QSize(48, 48);
case LowDPI:
return QSize(32, 32);
}
return QSize(72, 72);
}
void AndroidManifestEditorWidget::updateIconPath(const QString &newPath, IconDPI dpi)
{
switch (dpi) {
case HighDPI:
m_hIconPath = newPath;
break;
case MediumDPI:
m_mIconPath = newPath;
break;
case LowDPI:
m_lIconPath = newPath;
break;
}
}
QIcon AndroidManifestEditorWidget::icon(const QString &baseDir, IconDPI dpi) QIcon AndroidManifestEditorWidget::icon(const QString &baseDir, IconDPI dpi)
{ {
@@ -1250,90 +1300,177 @@ QIcon AndroidManifestEditorWidget::icon(const QString &baseDir, IconDPI dpi)
void AndroidManifestEditorWidget::copyIcon(IconDPI dpi, const QString &baseDir, const QString &filePath) void AndroidManifestEditorWidget::copyIcon(IconDPI dpi, const QString &baseDir, const QString &filePath)
{ {
if (!QFileInfo::exists(filePath))
return;
const QString targetPath = baseDir + iconPath(dpi); const QString targetPath = baseDir + iconPath(dpi);
QFile::remove(targetPath); if (targetPath.isEmpty()) {
QDir dir; qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot copy icon.";
dir.mkpath(QFileInfo(targetPath).absolutePath()); return;
QFile::copy(filePath, targetPath); }
QFileInfo targetFile(targetPath);
if (filePath == targetPath)
return;
removeIcon(dpi, baseDir);
QImage original(filePath);
if (!targetPath.isEmpty() && !original.isNull()) {
QDir dir;
dir.mkpath(QFileInfo(targetPath).absolutePath());
QSize targetSize = iconSize(dpi);
QImage scaled = original.scaled(targetSize.width(), targetSize.height(),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
toggleIconScaleWarning(dpi, scaled.width() > original.width() || scaled.height() > original.height());
scaled.save(targetPath);
updateIconPath(targetPath, dpi);
}
}
void AndroidManifestEditorWidget::removeIcon(IconDPI dpi, const QString &baseDir)
{
const QString targetPath = baseDir + iconPath(dpi);
if (targetPath.isEmpty()) {
qCDebug(androidManifestEditorLog) << "Icon target path empty, cannot remove icon.";
return;
}
QFileInfo targetFile(targetPath);
if (targetFile.exists()) {
QDir rmRf(targetFile.absoluteDir());
rmRf.removeRecursively();
}
toggleIconScaleWarning(dpi, false);
}
void AndroidManifestEditorWidget::toggleIconScaleWarning(IconDPI dpi, bool visible)
{
switch (dpi) {
case HighDPI:
m_hIconScaleWarningLabel->setVisible(visible);
break;
case MediumDPI:
m_mIconScaleWarningLabel->setVisible(visible);
break;
case LowDPI:
m_lIconScaleWarningLabel->setVisible(visible);
break;
}
}
const auto fileDialogIconFiles = QWidget::tr("Images (*.png *.jpg *.webp *.svg)");
void AndroidManifestEditorWidget::setMasterIcon()
{
QString file = QFileDialog::getOpenFileName(this, tr("Choose Master Icon"), QDir::homePath(), fileDialogIconFiles);
if (file.isEmpty())
return;
QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
copyIcon(LowDPI, baseDir, file);
copyIcon(MediumDPI, baseDir, file);
copyIcon(HighDPI, baseDir, file);
m_lIconButton->setIcon(icon(baseDir, LowDPI));
m_mIconButton->setIcon(icon(baseDir, MediumDPI));
m_hIconButton->setIcon(icon(baseDir, HighDPI));
} }
void AndroidManifestEditorWidget::setLDPIIcon() void AndroidManifestEditorWidget::setLDPIIcon()
{ {
QString file = QFileDialog::getOpenFileName(this, tr("Choose Low DPI Icon"), QDir::homePath(), tr("PNG images (*.png)")); QString file = QFileDialog::getOpenFileName(this, tr("Choose Low DPI Icon"), QDir::homePath(), fileDialogIconFiles);
if (file.isEmpty()) if (file.isEmpty())
return; return;
m_lIconPath = file; m_lIconPath = file;
m_lIconButton->setIcon(QIcon(file)); QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
setDirty(true); copyIcon(LowDPI, baseDir, m_lIconPath);
m_lIconButton->setIcon(icon(baseDir, LowDPI));
} }
void AndroidManifestEditorWidget::setMDPIIcon() void AndroidManifestEditorWidget::setMDPIIcon()
{ {
QString file = QFileDialog::getOpenFileName(this, tr("Choose Medium DPI Icon"), QDir::homePath(), tr("PNG images (*.png)")); QString file = QFileDialog::getOpenFileName(this, tr("Choose Medium DPI Icon"), QDir::homePath(), fileDialogIconFiles);
if (file.isEmpty()) if (file.isEmpty())
return; return;
m_mIconPath = file; m_mIconPath = file;
m_mIconButton->setIcon(QIcon(file)); QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
setDirty(true); copyIcon(MediumDPI, baseDir, m_mIconPath);
m_mIconButton->setIcon(icon(baseDir, MediumDPI));
} }
void AndroidManifestEditorWidget::setHDPIIcon() void AndroidManifestEditorWidget::setHDPIIcon()
{ {
QString file = QFileDialog::getOpenFileName(this, tr("Choose High DPI Icon"), QDir::homePath(), tr("PNG images (*.png)")); QString file = QFileDialog::getOpenFileName(this, tr("Choose High DPI Icon"), QDir::homePath(), fileDialogIconFiles);
if (file.isEmpty()) if (file.isEmpty())
return; return;
m_hIconPath = file; m_hIconPath = file;
m_hIconButton->setIcon(QIcon(file)); QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
setDirty(true); copyIcon(HighDPI, baseDir, m_hIconPath);
m_hIconButton->setIcon(icon(baseDir, HighDPI));
} }
void AndroidManifestEditorWidget::clearLDPIIcon() void AndroidManifestEditorWidget::clearLDPIIcon()
{ {
m_lIconPath.clear(); m_lIconPath.clear();
m_lIconButton->setIcon(QIcon()); m_lIconButton->setIcon(QIcon());
QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
removeIcon(LowDPI, baseDir);
} }
void AndroidManifestEditorWidget::clearMDPIIcon() void AndroidManifestEditorWidget::clearMDPIIcon()
{ {
m_mIconPath.clear(); m_mIconPath.clear();
m_mIconButton->setIcon(QIcon()); m_mIconButton->setIcon(QIcon());
QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
removeIcon(MediumDPI, baseDir);
} }
void AndroidManifestEditorWidget::clearHDPIIcon() void AndroidManifestEditorWidget::clearHDPIIcon()
{ {
m_hIconPath.clear(); m_hIconPath.clear();
m_hIconButton->setIcon(QIcon()); m_hIconButton->setIcon(QIcon());
QString baseDir = m_textEditorWidget->textDocument()->filePath().toFileInfo().absolutePath();
removeIcon(HighDPI, baseDir);
} }
void AndroidManifestEditorWidget::createDPIButton(QHBoxLayout *layout, void AndroidManifestEditorWidget::createDPIButton(QHBoxLayout *layout,
QWidget *parent, QWidget *parent,
QToolButton *&button, QToolButton *&button,
QToolButton *&clearButton, const QSize &buttonSize,
const QString &title, const QString &title,
const QString &tooltip) const QString &tooltip,
QToolButton **clearButton,
QLabel **scaleWarningLabel)
{ {
auto iconLayout = new QVBoxLayout(); auto iconLayout = new QVBoxLayout();
auto iconTitle = new QLabel(title, parent); auto iconTitle = new QLabel(title, parent);
auto iconButtonLayout = new QGridLayout(); auto iconButtonLayout = new QGridLayout();
button = new QToolButton(parent); button = new QToolButton(parent);
button->setMinimumSize(QSize(48, 48)); button->setMinimumSize(buttonSize);
button->setMaximumSize(QSize(48, 48)); button->setMaximumSize(buttonSize);
button->setToolTip(tooltip); button->setToolTip(tooltip);
clearButton = new QToolButton(parent); button->setIconSize(buttonSize);
clearButton->setMinimumSize(QSize(16, 16)); QSize clearAndWarningSize(16, 16);
clearButton->setMaximumSize(QSize(16, 16)); if (clearButton) {
clearButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); *clearButton = new QToolButton(parent);
(*clearButton)->setMinimumSize(clearAndWarningSize);
(*clearButton)->setMaximumSize(clearAndWarningSize);
(*clearButton)->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon());
}
if (scaleWarningLabel) {
*scaleWarningLabel = new QLabel(parent);
(*scaleWarningLabel)->setMinimumSize(clearAndWarningSize);
(*scaleWarningLabel)->setMaximumSize(clearAndWarningSize);
(*scaleWarningLabel)->setPixmap(Utils::Icons::WARNING.icon().pixmap(clearAndWarningSize));
(*scaleWarningLabel)->setToolTip(tr("Icon scaled up"));
(*scaleWarningLabel)->setVisible(false);
}
auto label = new QLabel(tr("Click to select"), parent); auto label = new QLabel(tr("Click to select"), parent);
iconLayout->addWidget(iconTitle); iconLayout->addWidget(iconTitle);
iconLayout->setAlignment(iconTitle, Qt::AlignHCenter); iconLayout->setAlignment(iconTitle, Qt::AlignHCenter);
iconButtonLayout->setColumnMinimumWidth(0, 16); iconButtonLayout->setColumnMinimumWidth(0, 16);
iconButtonLayout->addWidget(button, 0, 1, 1, 3); iconButtonLayout->addWidget(button, 0, 1, 1, 3);
iconButtonLayout->setAlignment(button, Qt::AlignVCenter); iconButtonLayout->setAlignment(button, Qt::AlignVCenter);
iconButtonLayout->addWidget(clearButton, 0, 4, 1, 1); if (clearButton) {
iconButtonLayout->setAlignment(clearButton, Qt::AlignTop); iconButtonLayout->addWidget(*clearButton, 0, 4, 1, 1);
iconButtonLayout->setAlignment(*clearButton, Qt::AlignTop);
}
if (scaleWarningLabel) {
iconButtonLayout->addWidget(*scaleWarningLabel, 0, 0, 1, 1);
iconButtonLayout->setAlignment(*scaleWarningLabel, Qt::AlignTop);
}
iconLayout->addLayout(iconButtonLayout); iconLayout->addLayout(iconButtonLayout);
iconLayout->setAlignment(iconButtonLayout, Qt::AlignHCenter); iconLayout->setAlignment(iconButtonLayout, Qt::AlignHCenter);
iconLayout->addWidget(label); iconLayout->addWidget(label);

View File

@@ -116,6 +116,8 @@ protected:
void focusInEvent(QFocusEvent *event) override; void focusInEvent(QFocusEvent *event) override;
private: private:
void setMasterIcon();
void clearMasterIcon();
void setLDPIIcon(); void setLDPIIcon();
void setMDPIIcon(); void setMDPIIcon();
void setHDPIIcon(); void setHDPIIcon();
@@ -124,8 +126,11 @@ private:
void clearHDPIIcon(); void clearHDPIIcon();
void createDPIButton(QHBoxLayout *layout, void createDPIButton(QHBoxLayout *layout,
QWidget *parent, QWidget *parent,
QToolButton *&button, QToolButton *&clearButton, QToolButton *&button, const QSize &buttonSize,
const QString &title, const QString &tooltip); const QString &title, const QString &tooltip,
QToolButton **clearButton = nullptr,
QLabel **scaleWarningLabel = nullptr
);
void defaultPermissionOrFeatureCheckBoxClicked(); void defaultPermissionOrFeatureCheckBoxClicked();
void addPermission(); void addPermission();
void removePermission(); void removePermission();
@@ -146,7 +151,11 @@ private:
enum IconDPI { LowDPI, MediumDPI, HighDPI }; enum IconDPI { LowDPI, MediumDPI, HighDPI };
QIcon icon(const QString &baseDir, IconDPI dpi); QIcon icon(const QString &baseDir, IconDPI dpi);
QString iconPath(IconDPI dpi); QString iconPath(IconDPI dpi);
QSize iconSize(IconDPI dpi);
void updateIconPath(const QString &newPath, IconDPI dpi);
void copyIcon(IconDPI dpi, const QString &baseDir, const QString &filePath); void copyIcon(IconDPI dpi, const QString &baseDir, const QString &filePath);
void removeIcon(IconDPI dpi, const QString &baseDir);
void toggleIconScaleWarning(IconDPI dpi, bool visible);
void updateInfoBar(const QString &errorMessage, int line, int column); void updateInfoBar(const QString &errorMessage, int line, int column);
void hideInfoBar(); void hideInfoBar();
@@ -180,13 +189,17 @@ private:
QLineEdit *m_appNameLineEdit; QLineEdit *m_appNameLineEdit;
QLineEdit *m_activityNameLineEdit; QLineEdit *m_activityNameLineEdit;
QComboBox *m_targetLineEdit; QComboBox *m_targetLineEdit;
QToolButton *m_masterIconButton;
QToolButton *m_lIconButton; QToolButton *m_lIconButton;
QToolButton *m_lIconClearButton; QToolButton *m_lIconClearButton;
QLabel *m_lIconScaleWarningLabel;
QToolButton *m_mIconButton; QToolButton *m_mIconButton;
QToolButton *m_mIconClearButton; QToolButton *m_mIconClearButton;
QLabel *m_mIconScaleWarningLabel;
QToolButton *m_hIconButton; QToolButton *m_hIconButton;
QToolButton *m_hIconClearButton; QToolButton *m_hIconClearButton;
QString m_lIconPath; // only set if the user changed the icon QLabel *m_hIconScaleWarningLabel;
QString m_lIconPath;
QString m_mIconPath; QString m_mIconPath;
QString m_hIconPath; QString m_hIconPath;