InsertImageDialog: support scaling image

This commit is contained in:
Le Tan 2018-09-30 23:01:11 +08:00
parent 5cc8d6c8f1
commit 9923feea14
4 changed files with 202 additions and 87 deletions

View File

@ -14,7 +14,6 @@ VInsertImageDialog::VInsertImageDialog(const QString &p_title,
bool p_browsable, bool p_browsable,
QWidget *p_parent) QWidget *p_parent)
: QDialog(p_parent), : QDialog(p_parent),
m_image(NULL),
m_browsable(p_browsable) m_browsable(p_browsable)
{ {
setupUI(p_title, p_imageTitle, p_imagePath); setupUI(p_title, p_imageTitle, p_imagePath);
@ -35,40 +34,81 @@ VInsertImageDialog::VInsertImageDialog(const QString &p_title,
handleInputChanged(); handleInputChanged();
} }
VInsertImageDialog::~VInsertImageDialog()
{
delete m_image;
m_image = NULL;
}
void VInsertImageDialog::setupUI(const QString &p_title, void VInsertImageDialog::setupUI(const QString &p_title,
const QString &p_imageTitle, const QString &p_imageTitle,
const QString &p_imagePath) const QString &p_imagePath)
{ {
QLabel *pathLabel = new QLabel(tr("&From:")); // Path.
m_pathEdit = new VLineEdit(p_imagePath); m_pathEdit = new VLineEdit(p_imagePath);
pathLabel->setBuddy(m_pathEdit);
browseBtn = new QPushButton(tr("&Browse"));
m_pathEdit->setReadOnly(!m_browsable); m_pathEdit->setReadOnly(!m_browsable);
browseBtn = new QPushButton(tr("&Browse"));
browseBtn->setEnabled(m_browsable); browseBtn->setEnabled(m_browsable);
QLabel *imageTitleLabel = new QLabel(tr("&Image title:")); // Title.
m_imageTitleEdit = new VMetaWordLineEdit(p_imageTitle); m_imageTitleEdit = new VMetaWordLineEdit(p_imageTitle);
m_imageTitleEdit->selectAll(); m_imageTitleEdit->selectAll();
imageTitleLabel->setBuddy(m_imageTitleEdit);
QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp), QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp),
m_imageTitleEdit); m_imageTitleEdit);
m_imageTitleEdit->setValidator(validator); 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<void(QSpinBox::*)(int)>(&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(); QGridLayout *topLayout = new QGridLayout();
topLayout->addWidget(pathLabel, 0, 0); topLayout->addWidget(new QLabel(tr("From:")), 0, 0, 1, 1);
topLayout->addWidget(m_pathEdit, 0, 1); topLayout->addWidget(m_pathEdit, 0, 1, 1, 3);
topLayout->addWidget(browseBtn, 0, 2); topLayout->addWidget(browseBtn, 0, 4, 1, 1);
topLayout->addWidget(imageTitleLabel, 1, 0); topLayout->addWidget(new QLabel(tr("Title:")), 1, 0, 1, 1);
topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 2); 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(0, 0);
topLayout->setColumnStretch(1, 1); topLayout->setColumnStretch(1, 0);
topLayout->setColumnStretch(2, 0); topLayout->setColumnStretch(2, 1);
topLayout->setColumnStretch(3, 1);
topLayout->setColumnStretch(4, 0);
// Ok is the default button. // Ok is the default button.
m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 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); connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true); m_btnBox->button(QDialogButtonBox::Ok)->setProperty("SpecialBtn", true);
imagePreviewLabel = new QLabel(); m_imageLabel = new QLabel();
imagePreviewLabel->setVisible(false); 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(); QVBoxLayout *mainLayout = new QVBoxLayout();
mainLayout->addLayout(topLayout); mainLayout->addLayout(topLayout);
mainLayout->addWidget(m_btnBox); mainLayout->addWidget(m_btnBox);
mainLayout->addWidget(imagePreviewLabel); mainLayout->addWidget(m_previewArea);
setLayout(mainLayout); setLayout(mainLayout);
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
setWindowTitle(p_title); setWindowTitle(p_title);
m_imageTitleEdit->setFocus(); m_imageTitleEdit->setFocus();
@ -133,34 +181,29 @@ void VInsertImageDialog::handleBrowseBtnClicked()
m_imageTitleEdit->setFocus(); m_imageTitleEdit->setFocus();
} }
void VInsertImageDialog::setImage(const QImage &image) void VInsertImageDialog::setImage(const QImage &p_image)
{ {
if (image.isNull()) { if (p_image.isNull()) {
imagePreviewLabel->setVisible(false); m_image.clear();
delete m_image; m_imageLabel->clear();
m_image = NULL;
handleInputChanged(); setImageControlsVisible(false);
return;
}
int width = 512 * VUtils::calculateScaleFactor();
QSize previewSize(width, width);
if (!m_image) {
m_image = new QImage(image);
} else { } else {
*m_image = image; m_image.reset(new QImage(p_image));
}
QPixmap pixmap; m_imageLabel->setPixmap(QPixmap::fromImage(*m_image));
if (image.width() > width || image.height() > width) {
pixmap = QPixmap::fromImage(m_image->scaled(previewSize, Qt::KeepAspectRatio));
} else {
pixmap = QPixmap::fromImage(*m_image);
}
imagePreviewLabel->setPixmap(pixmap); m_imageLabel->adjustSize();
imagePreviewLabel->setVisible(true);
// 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(); handleInputChanged();
} }
@ -281,3 +324,22 @@ void VInsertImageDialog::setPath(const QString &p_path)
m_pathEdit->setText(p_path); m_pathEdit->setText(p_path);
handlePathEditChanged(); 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;
}

View File

@ -13,6 +13,9 @@ class VLineEdit;
class VMetaWordLineEdit; class VMetaWordLineEdit;
class QPushButton; class QPushButton;
class QDialogButtonBox; class QDialogButtonBox;
class QScrollArea;
class QSpinBox;
class QSlider;
class VInsertImageDialog : public QDialog class VInsertImageDialog : public QDialog
{ {
@ -30,18 +33,19 @@ public:
bool p_browsable = true, bool p_browsable = true,
QWidget *p_parent = nullptr); QWidget *p_parent = nullptr);
~VInsertImageDialog();
QString getImageTitleInput() const; QString getImageTitleInput() const;
QString getPathInput() const; QString getPathInput() const;
void setImage(const QImage &image); void setImage(const QImage &p_image);
QImage getImage() const; QImage getImage() const;
VInsertImageDialog::ImageType getImageType() const; VInsertImageDialog::ImageType getImageType() const;
// Return 0 if no override.
int getOverridenWidth() const;
public slots: public slots:
void imageDownloaded(const QByteArray &data); void imageDownloaded(const QByteArray &data);
@ -61,13 +65,22 @@ private:
void setPath(const QString &p_path); void setPath(const QString &p_path);
void setImageControlsVisible(bool p_visible);
VMetaWordLineEdit *m_imageTitleEdit; VMetaWordLineEdit *m_imageTitleEdit;
VLineEdit *m_pathEdit; VLineEdit *m_pathEdit;
QPushButton *browseBtn; 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<QImage> m_image;
// Whether enable the browse action. // Whether enable the browse action.
bool m_browsable; bool m_browsable;

View File

@ -52,28 +52,51 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
insertImageFromQImage(dialog.getImageTitleInput(), insertImageFromQImage(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), m_file->getImageFolderInLink(),
image); image,
dialog.getOverridenWidth());
} }
return true; return true;
} }
void VMdEditOperations::insertImageFromQImage(const QString &title, // @p_width, @p_height: 0 if no override.
const QString &path, static QString imageLink(const QString &p_title,
const QString &folderInLink, const QString &p_url,
const QImage &image) int p_width = 0,
int p_height = 0)
{ {
QString fileName = VUtils::generateImageFileName(path, title); QString scale;
QString filePath = QDir(path).filePath(fileName); 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()); V_ASSERT(!QFile(filePath).exists());
QString errStr; QString errStr;
bool ret = VUtils::makePath(path); bool ret = VUtils::makePath(p_folderPath);
if (!ret) { if (!ret) {
errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.") errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle).arg(path); .arg(g_config->c_dataTextStyle).arg(p_folderPath);
} else { } else {
ret = image.save(filePath); ret = p_image.save(filePath);
if (!ret) { if (!ret) {
errStr = tr("Fail to save image <span style=\"%1\">%2</span>.") errStr = tr("Fail to save image <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle).arg(filePath); .arg(g_config->c_dataTextStyle).arg(filePath);
@ -82,7 +105,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
if (!ret) { if (!ret) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"), VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(title), tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(p_title),
errStr, errStr,
QMessageBox::Ok, QMessageBox::Ok,
QMessageBox::Ok, QMessageBox::Ok,
@ -90,11 +113,11 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
return; return;
} }
QString url = QString("%1/%2").arg(folderInLink).arg(fileName); QString url = QString("%1/%2").arg(p_folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url);
insertText(md);
qDebug() << "insert image" << title << filePath; insertText(imageLink(p_title, url, p_width, p_height));
qDebug() << "insert image" << p_title << filePath;
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor); VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
Q_ASSERT(mdEditor); Q_ASSERT(mdEditor);
@ -104,7 +127,9 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
void VMdEditOperations::insertImageFromPath(const QString &p_title, void VMdEditOperations::insertImageFromPath(const QString &p_title,
const QString &p_folderPath, const QString &p_folderPath,
const QString &p_folderInLink, const QString &p_folderInLink,
const QString &p_srcImagePath) const QString &p_srcImagePath,
int p_width,
int p_height)
{ {
insertImageFromPath(p_title, insertImageFromPath(p_title,
p_folderPath, p_folderPath,
@ -112,7 +137,9 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title,
p_srcImagePath, p_srcImagePath,
true, true,
QString(), QString(),
QString()); QString(),
p_width,
p_height);
} }
void VMdEditOperations::insertImageFromPath(const QString &p_title, void VMdEditOperations::insertImageFromPath(const QString &p_title,
@ -121,7 +148,9 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title,
const QString &p_srcImagePath, const QString &p_srcImagePath,
bool p_insertText, bool p_insertText,
QString &p_destImagePath, QString &p_destImagePath,
QString &p_urlInLink) QString &p_urlInLink,
int p_width,
int p_height)
{ {
p_destImagePath.clear(); p_destImagePath.clear();
p_urlInLink.clear(); p_urlInLink.clear();
@ -161,8 +190,7 @@ void VMdEditOperations::insertImageFromPath(const QString &p_title,
p_destImagePath = filePath; p_destImagePath = filePath;
if (p_insertText) { if (p_insertText) {
QString md = QString("![%1](%2)").arg(p_title).arg(p_urlInLink); insertText(imageLink(p_title, p_urlInLink, p_width, p_height));
insertText(md);
} }
qDebug() << "insert image" << p_title << filePath; qDebug() << "insert image" << p_title << filePath;
@ -214,18 +242,21 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
insertImageFromPath(dialog.getImageTitleInput(), insertImageFromPath(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), m_file->getImageFolderInLink(),
imagePath); imagePath,
dialog.getOverridenWidth());
} else { } else {
if (dialog.getImageType() == VInsertImageDialog::ImageType::LocalFile) { if (dialog.getImageType() == VInsertImageDialog::ImageType::LocalFile) {
insertImageFromPath(dialog.getImageTitleInput(), insertImageFromPath(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), m_file->getImageFolderInLink(),
dialog.getPathInput()); dialog.getPathInput(),
dialog.getOverridenWidth());
} else { } else {
insertImageFromQImage(dialog.getImageTitleInput(), insertImageFromQImage(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), m_file->getImageFolderInLink(),
dialog.getImage()); dialog.getImage(),
dialog.getOverridenWidth());
} }
} }
} }
@ -246,14 +277,16 @@ bool VMdEditOperations::insertImage()
insertImageFromPath(dialog.getImageTitleInput(), insertImageFromPath(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), m_file->getImageFolderInLink(),
dialog.getPathInput()); dialog.getPathInput(),
dialog.getOverridenWidth());
} else { } else {
QImage img = dialog.getImage(); QImage img = dialog.getImage();
if (!img.isNull()) { if (!img.isNull()) {
insertImageFromQImage(dialog.getImageTitleInput(), insertImageFromQImage(dialog.getImageTitleInput(),
m_file->fetchImageFolderPath(), m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(), 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, bool VMdEditOperations::insertImageLink(const QString &p_linkText,
const QString &p_linkUrl) const QString &p_linkUrl)
{ {
QString link = QString("![%1](%2)").arg(p_linkText).arg(p_linkUrl);
QTextCursor cursor = m_editor->textCursorW(); QTextCursor cursor = m_editor->textCursorW();
cursor.insertText(link); cursor.insertText(imageLink(p_linkText, p_linkUrl));
m_editor->setTextCursorW(cursor); m_editor->setTextCursorW(cursor);
setVimMode(VimMode::Insert); setVimMode(VimMode::Insert);

View File

@ -41,7 +41,9 @@ public:
const QString &p_srcImagePath, const QString &p_srcImagePath,
bool p_insertText, bool p_insertText,
QString &p_destImagePath, QString &p_destImagePath,
QString &p_urlInLink); QString &p_urlInLink,
int p_width = 0,
int p_height = 0);
private: private:
// Insert image from @p_srcImagePath as to @p_folderPath. // Insert image from @p_srcImagePath as to @p_folderPath.
@ -49,14 +51,20 @@ private:
void insertImageFromPath(const QString &p_title, void insertImageFromPath(const QString &p_title,
const QString &p_folderPath, const QString &p_folderPath,
const QString &p_folderInLink, 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; // @p_title: title of the inserted image;
// @path: the image folder path to insert the image in; // @p_folderPath: the image folder path to insert the image in;
// @folderInLink: the folder part in the image link. // @p_folderInLink: the folder part in the image link.
// @image: the image to be inserted; // @p_image: the image to be inserted;
void insertImageFromQImage(const QString &title, const QString &path, void insertImageFromQImage(const QString &p_title,
const QString &folderInLink, const QImage &image); const QString &p_folderPath,
const QString &p_folderInLink,
const QImage &p_image,
int p_width = 0,
int p_height = 0);
// Key press handlers. // Key press handlers.
bool handleKeyTab(QKeyEvent *p_event); bool handleKeyTab(QKeyEvent *p_event);