From 9923feea148396c7daea77010eb0735fa339b468 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sun, 30 Sep 2018 23:01:11 +0800 Subject: [PATCH] InsertImageDialog: support scaling image --- src/dialog/vinsertimagedialog.cpp | 154 +++++++++++++++++++++--------- src/dialog/vinsertimagedialog.h | 25 +++-- src/vmdeditoperations.cpp | 86 +++++++++++------ src/vmdeditoperations.h | 24 +++-- 4 files changed, 202 insertions(+), 87 deletions(-) diff --git a/src/dialog/vinsertimagedialog.cpp b/src/dialog/vinsertimagedialog.cpp index 1f6884a0..b014802b 100644 --- a/src/dialog/vinsertimagedialog.cpp +++ b/src/dialog/vinsertimagedialog.cpp @@ -14,7 +14,6 @@ VInsertImageDialog::VInsertImageDialog(const QString &p_title, bool p_browsable, QWidget *p_parent) : QDialog(p_parent), - m_image(NULL), m_browsable(p_browsable) { setupUI(p_title, p_imageTitle, p_imagePath); @@ -35,40 +34,81 @@ VInsertImageDialog::VInsertImageDialog(const QString &p_title, handleInputChanged(); } -VInsertImageDialog::~VInsertImageDialog() -{ - delete m_image; - m_image = NULL; -} - void VInsertImageDialog::setupUI(const QString &p_title, const QString &p_imageTitle, const QString &p_imagePath) { - QLabel *pathLabel = new QLabel(tr("&From:")); + // Path. m_pathEdit = new VLineEdit(p_imagePath); - pathLabel->setBuddy(m_pathEdit); - browseBtn = new QPushButton(tr("&Browse")); m_pathEdit->setReadOnly(!m_browsable); + browseBtn = new QPushButton(tr("&Browse")); browseBtn->setEnabled(m_browsable); - QLabel *imageTitleLabel = new QLabel(tr("&Image title:")); + // Title. m_imageTitleEdit = new VMetaWordLineEdit(p_imageTitle); m_imageTitleEdit->selectAll(); - imageTitleLabel->setBuddy(m_imageTitleEdit); QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp), m_imageTitleEdit); m_imageTitleEdit->setValidator(validator); + // Scale. + m_widthSpin = new QSpinBox(); + m_widthSpin->setMinimum(1); + m_widthSpin->setSingleStep(10); + m_widthSpin->setSuffix(" px"); + connect(m_widthSpin, static_cast(&QSpinBox::valueChanged), + this, [this](int p_val) { + if (!m_image) { + return; + } + + int height = m_image->height() * (1.0 * p_val / m_image->width()); + m_imageLabel->resize(p_val, height); + }); + + // 0.1 to 2.0 -> 1 to 20. + m_scaleSlider = new QSlider(); + m_scaleSlider->setOrientation(Qt::Horizontal); + m_scaleSlider->setMinimum(1); + m_scaleSlider->setMaximum(20); + m_scaleSlider->setValue(10); + m_scaleSlider->setSingleStep(1); + m_scaleSlider->setPageStep(5); + connect(m_scaleSlider, &QSlider::valueChanged, + this, [this](int p_val) { + if (!m_image) { + return; + } + + int width = m_image->width(); + qreal factor = 1.0; + if (p_val != 10) { + factor = p_val / 10.0; + width = m_image->width() * factor; + } + + m_widthSpin->setValue(width); + m_sliderLabel->setText(QString::number(factor) + "x"); + }); + + m_sliderLabel = new QLabel("1x"); + QGridLayout *topLayout = new QGridLayout(); - topLayout->addWidget(pathLabel, 0, 0); - topLayout->addWidget(m_pathEdit, 0, 1); - topLayout->addWidget(browseBtn, 0, 2); - topLayout->addWidget(imageTitleLabel, 1, 0); - topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 2); + topLayout->addWidget(new QLabel(tr("From:")), 0, 0, 1, 1); + topLayout->addWidget(m_pathEdit, 0, 1, 1, 3); + topLayout->addWidget(browseBtn, 0, 4, 1, 1); + topLayout->addWidget(new QLabel(tr("Title:")), 1, 0, 1, 1); + topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 4); + topLayout->addWidget(new QLabel(tr("Scaling width:")), 2, 0, 1, 1); + topLayout->addWidget(m_widthSpin, 2, 1, 1, 1); + topLayout->addWidget(m_scaleSlider, 2, 2, 1, 2); + topLayout->addWidget(m_sliderLabel, 2, 4, 1, 1); + topLayout->setColumnStretch(0, 0); - topLayout->setColumnStretch(1, 1); - topLayout->setColumnStretch(2, 0); + topLayout->setColumnStretch(1, 0); + topLayout->setColumnStretch(2, 1); + topLayout->setColumnStretch(3, 1); + topLayout->setColumnStretch(4, 0); // Ok is the default button. m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -76,15 +116,23 @@ void VInsertImageDialog::setupUI(const QString &p_title, connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject); m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true); - imagePreviewLabel = new QLabel(); - imagePreviewLabel->setVisible(false); + m_imageLabel = new QLabel(); + m_imageLabel->setScaledContents(true); + + m_previewArea = new QScrollArea(); + m_previewArea->setBackgroundRole(QPalette::Dark); + m_previewArea->setWidget(m_imageLabel); + int minWidth = 512 * VUtils::calculateScaleFactor(); + m_previewArea->setMinimumSize(minWidth, minWidth); + + setImageControlsVisible(false); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addLayout(topLayout); mainLayout->addWidget(m_btnBox); - mainLayout->addWidget(imagePreviewLabel); + mainLayout->addWidget(m_previewArea); setLayout(mainLayout); - mainLayout->setSizeConstraint(QLayout::SetFixedSize); + setWindowTitle(p_title); m_imageTitleEdit->setFocus(); @@ -133,34 +181,29 @@ void VInsertImageDialog::handleBrowseBtnClicked() m_imageTitleEdit->setFocus(); } -void VInsertImageDialog::setImage(const QImage &image) +void VInsertImageDialog::setImage(const QImage &p_image) { - if (image.isNull()) { - imagePreviewLabel->setVisible(false); - delete m_image; - m_image = NULL; + if (p_image.isNull()) { + m_image.clear(); + m_imageLabel->clear(); - handleInputChanged(); - return; - } - - int width = 512 * VUtils::calculateScaleFactor(); - QSize previewSize(width, width); - if (!m_image) { - m_image = new QImage(image); + setImageControlsVisible(false); } else { - *m_image = image; - } + m_image.reset(new QImage(p_image)); - QPixmap pixmap; - if (image.width() > width || image.height() > width) { - pixmap = QPixmap::fromImage(m_image->scaled(previewSize, Qt::KeepAspectRatio)); - } else { - pixmap = QPixmap::fromImage(*m_image); - } + m_imageLabel->setPixmap(QPixmap::fromImage(*m_image)); - imagePreviewLabel->setPixmap(pixmap); - imagePreviewLabel->setVisible(true); + m_imageLabel->adjustSize(); + + // Set the scaling widgets. + m_scaleSlider->setValue(10); + + int width = m_image->width(); + m_widthSpin->setMaximum(width * 5); + m_widthSpin->setValue(width); + + setImageControlsVisible(true); + } handleInputChanged(); } @@ -281,3 +324,22 @@ void VInsertImageDialog::setPath(const QString &p_path) m_pathEdit->setText(p_path); handlePathEditChanged(); } + +void VInsertImageDialog::setImageControlsVisible(bool p_visible) +{ + m_widthSpin->setEnabled(p_visible); + m_scaleSlider->setEnabled(p_visible); + m_sliderLabel->setEnabled(p_visible); + + m_previewArea->setVisible(p_visible); +} + +int VInsertImageDialog::getOverridenWidth() const +{ + int width = m_widthSpin->value(); + if (m_image && m_image->width() != width) { + return width; + } + + return 0; +} diff --git a/src/dialog/vinsertimagedialog.h b/src/dialog/vinsertimagedialog.h index dc6e6977..8ab9f32c 100644 --- a/src/dialog/vinsertimagedialog.h +++ b/src/dialog/vinsertimagedialog.h @@ -13,6 +13,9 @@ class VLineEdit; class VMetaWordLineEdit; class QPushButton; class QDialogButtonBox; +class QScrollArea; +class QSpinBox; +class QSlider; class VInsertImageDialog : public QDialog { @@ -30,18 +33,19 @@ public: bool p_browsable = true, QWidget *p_parent = nullptr); - ~VInsertImageDialog(); - QString getImageTitleInput() const; QString getPathInput() const; - void setImage(const QImage &image); + void setImage(const QImage &p_image); QImage getImage() const; VInsertImageDialog::ImageType getImageType() const; + // Return 0 if no override. + int getOverridenWidth() const; + public slots: void imageDownloaded(const QByteArray &data); @@ -61,13 +65,22 @@ private: void setPath(const QString &p_path); + void setImageControlsVisible(bool p_visible); + VMetaWordLineEdit *m_imageTitleEdit; VLineEdit *m_pathEdit; QPushButton *browseBtn; - QDialogButtonBox *m_btnBox; - QLabel *imagePreviewLabel; - QImage *m_image; + QSpinBox *m_widthSpin; + QSlider *m_scaleSlider; + QLabel *m_sliderLabel; + + QDialogButtonBox *m_btnBox; + + QLabel *m_imageLabel; + QScrollArea *m_previewArea; + + QSharedPointer m_image; // Whether enable the browse action. bool m_browsable; diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 46c42100..b93c6dc0 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -52,28 +52,51 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source) insertImageFromQImage(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - image); + image, + dialog.getOverridenWidth()); } return true; } -void VMdEditOperations::insertImageFromQImage(const QString &title, - const QString &path, - const QString &folderInLink, - const QImage &image) +// @p_width, @p_height: 0 if no override. +static QString imageLink(const QString &p_title, + const QString &p_url, + int p_width = 0, + int p_height = 0) { - QString fileName = VUtils::generateImageFileName(path, title); - QString filePath = QDir(path).filePath(fileName); + QString scale; + if (p_width > 0) { + if (p_height > 0) { + scale = QString(" =%1x%2").arg(p_width).arg(p_height); + } else { + scale = QString(" =%1x").arg(p_width); + } + } else if (p_height > 0) { + scale = QString(" =x%1").arg(p_height); + } + + return QString("![%1](%2%3)").arg(p_title).arg(p_url).arg(scale); +} + +void VMdEditOperations::insertImageFromQImage(const QString &p_title, + const QString &p_folderPath, + const QString &p_folderInLink, + const QImage &p_image, + int p_width, + int p_height) +{ + QString fileName = VUtils::generateImageFileName(p_folderPath, p_title); + QString filePath = QDir(p_folderPath).filePath(fileName); V_ASSERT(!QFile(filePath).exists()); QString errStr; - bool ret = VUtils::makePath(path); + bool ret = VUtils::makePath(p_folderPath); if (!ret) { errStr = tr("Fail to create image folder %2.") - .arg(g_config->c_dataTextStyle).arg(path); + .arg(g_config->c_dataTextStyle).arg(p_folderPath); } else { - ret = image.save(filePath); + ret = p_image.save(filePath); if (!ret) { errStr = tr("Fail to save image %2.") .arg(g_config->c_dataTextStyle).arg(filePath); @@ -82,7 +105,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, if (!ret) { VUtils::showMessage(QMessageBox::Warning, tr("Warning"), - tr("Fail to insert image %2.").arg(g_config->c_dataTextStyle).arg(title), + tr("Fail to insert image %2.").arg(g_config->c_dataTextStyle).arg(p_title), errStr, QMessageBox::Ok, QMessageBox::Ok, @@ -90,11 +113,11 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, return; } - QString url = QString("%1/%2").arg(folderInLink).arg(fileName); - QString md = QString("![%1](%2)").arg(title).arg(url); - insertText(md); + QString url = QString("%1/%2").arg(p_folderInLink).arg(fileName); - qDebug() << "insert image" << title << filePath; + insertText(imageLink(p_title, url, p_width, p_height)); + + qDebug() << "insert image" << p_title << filePath; VMdEditor *mdEditor = dynamic_cast(m_editor); Q_ASSERT(mdEditor); @@ -104,7 +127,9 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, void VMdEditOperations::insertImageFromPath(const QString &p_title, const QString &p_folderPath, const QString &p_folderInLink, - const QString &p_srcImagePath) + const QString &p_srcImagePath, + int p_width, + int p_height) { insertImageFromPath(p_title, p_folderPath, @@ -112,7 +137,9 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title, p_srcImagePath, true, QString(), - QString()); + QString(), + p_width, + p_height); } void VMdEditOperations::insertImageFromPath(const QString &p_title, @@ -121,7 +148,9 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title, const QString &p_srcImagePath, bool p_insertText, QString &p_destImagePath, - QString &p_urlInLink) + QString &p_urlInLink, + int p_width, + int p_height) { p_destImagePath.clear(); p_urlInLink.clear(); @@ -161,8 +190,7 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title, p_destImagePath = filePath; if (p_insertText) { - QString md = QString("![%1](%2)").arg(p_title).arg(p_urlInLink); - insertText(md); + insertText(imageLink(p_title, p_urlInLink, p_width, p_height)); } qDebug() << "insert image" << p_title << filePath; @@ -214,18 +242,21 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl) insertImageFromPath(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - imagePath); + imagePath, + dialog.getOverridenWidth()); } else { if (dialog.getImageType() == VInsertImageDialog::ImageType::LocalFile) { insertImageFromPath(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - dialog.getPathInput()); + dialog.getPathInput(), + dialog.getOverridenWidth()); } else { insertImageFromQImage(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - dialog.getImage()); + dialog.getImage(), + dialog.getOverridenWidth()); } } } @@ -246,14 +277,16 @@ bool VMdEditOperations::insertImage() insertImageFromPath(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - dialog.getPathInput()); + dialog.getPathInput(), + dialog.getOverridenWidth()); } else { QImage img = dialog.getImage(); if (!img.isNull()) { insertImageFromQImage(dialog.getImageTitleInput(), m_file->fetchImageFolderPath(), m_file->getImageFolderInLink(), - img); + img, + dialog.getOverridenWidth()); } } } @@ -1168,9 +1201,8 @@ bool VMdEditOperations::insertLink(const QString &p_linkText, bool VMdEditOperations::insertImageLink(const QString &p_linkText, const QString &p_linkUrl) { - QString link = QString("![%1](%2)").arg(p_linkText).arg(p_linkUrl); QTextCursor cursor = m_editor->textCursorW(); - cursor.insertText(link); + cursor.insertText(imageLink(p_linkText, p_linkUrl)); m_editor->setTextCursorW(cursor); setVimMode(VimMode::Insert); diff --git a/src/vmdeditoperations.h b/src/vmdeditoperations.h index ca697b48..3c932fc8 100644 --- a/src/vmdeditoperations.h +++ b/src/vmdeditoperations.h @@ -41,7 +41,9 @@ public: const QString &p_srcImagePath, bool p_insertText, QString &p_destImagePath, - QString &p_urlInLink); + QString &p_urlInLink, + int p_width = 0, + int p_height = 0); private: // Insert image from @p_srcImagePath as to @p_folderPath. @@ -49,14 +51,20 @@ private: void insertImageFromPath(const QString &p_title, const QString &p_folderPath, const QString &p_folderInLink, - const QString &p_srcImagePath); + const QString &p_srcImagePath, + int p_width = 0, + int p_height = 0); - // @title: title of the inserted image; - // @path: the image folder path to insert the image in; - // @folderInLink: the folder part in the image link. - // @image: the image to be inserted; - void insertImageFromQImage(const QString &title, const QString &path, - const QString &folderInLink, const QImage &image); + // @p_title: title of the inserted image; + // @p_folderPath: the image folder path to insert the image in; + // @p_folderInLink: the folder part in the image link. + // @p_image: the image to be inserted; + void insertImageFromQImage(const QString &p_title, + const QString &p_folderPath, + const QString &p_folderInLink, + const QImage &p_image, + int p_width = 0, + int p_height = 0); // Key press handlers. bool handleKeyTab(QKeyEvent *p_event);