mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
VTextEdit: support previewing inline images
This commit is contained in:
parent
3e7fa998ee
commit
d2ee3e66d6
@ -23,20 +23,44 @@ bool VImageResourceManager2::contains(const QString &p_name) const
|
|||||||
QSet<int> VImageResourceManager2::updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo)
|
QSet<int> VImageResourceManager2::updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo)
|
||||||
{
|
{
|
||||||
QSet<QString> usedImages;
|
QSet<QString> usedImages;
|
||||||
QHash<int, VBlockImageInfo2> newBlocksInfo;
|
QHash<int, QVector<VBlockImageInfo2>> newBlocksInfo;
|
||||||
|
|
||||||
for (auto const & info : p_blocksInfo) {
|
for (auto const & info : p_blocksInfo) {
|
||||||
auto it = newBlocksInfo.insert(info.m_blockNumber, info);
|
VBlockImageInfo2 *newInfo = NULL;
|
||||||
VBlockImageInfo2 &newInfo = it.value();
|
auto blockIt = newBlocksInfo.find(info.m_blockNumber);
|
||||||
if (newInfo.m_padding < 0) {
|
if (blockIt == newBlocksInfo.end()) {
|
||||||
newInfo.m_padding = 0;
|
// New block.
|
||||||
|
QVector<VBlockImageInfo2> vec(1, info);
|
||||||
|
auto it = newBlocksInfo.insert(info.m_blockNumber, vec);
|
||||||
|
newInfo = &it.value().last();
|
||||||
|
} else {
|
||||||
|
// Multiple images for a block.
|
||||||
|
QVector<VBlockImageInfo2> &vec = blockIt.value();
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < vec.size(); ++i) {
|
||||||
|
Q_ASSERT(vec[i].m_blockNumber == info.m_blockNumber);
|
||||||
|
if (info < vec[i]) {
|
||||||
|
vec.insert(i, info);
|
||||||
|
newInfo = &vec[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto imageIt = m_images.find(newInfo.m_imageName);
|
if (i == vec.size()) {
|
||||||
|
vec.append(info);
|
||||||
|
newInfo = &vec.last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newInfo->m_padding < 0) {
|
||||||
|
newInfo->m_padding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto imageIt = m_images.find(newInfo->m_imageName);
|
||||||
if (imageIt != m_images.end()) {
|
if (imageIt != m_images.end()) {
|
||||||
// Fill the width and height.
|
// Fill the width and height.
|
||||||
newInfo.m_imageSize = imageIt.value().size();
|
newInfo->m_imageSize = imageIt.value().size();
|
||||||
usedImages.insert(newInfo.m_imageName);
|
usedImages.insert(newInfo->m_imageName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +85,7 @@ QSet<int> VImageResourceManager2::updateBlockInfos(const QVector<VBlockImageInfo
|
|||||||
return affectedBlocks;
|
return affectedBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VBlockImageInfo2 *VImageResourceManager2::findImageInfoByBlock(int p_blockNumber) const
|
const QVector<VBlockImageInfo2> *VImageResourceManager2::findImageInfoByBlock(int p_blockNumber) const
|
||||||
{
|
{
|
||||||
auto it = m_blocksInfo.find(p_blockNumber);
|
auto it = m_blocksInfo.find(p_blockNumber);
|
||||||
if (it != m_blocksInfo.end()) {
|
if (it != m_blocksInfo.end()) {
|
||||||
|
@ -28,7 +28,7 @@ public:
|
|||||||
// Return changed blocks' block number.
|
// Return changed blocks' block number.
|
||||||
QSet<int> updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo);
|
QSet<int> updateBlockInfos(const QVector<VBlockImageInfo2> &p_blocksInfo);
|
||||||
|
|
||||||
const VBlockImageInfo2 *findImageInfoByBlock(int p_blockNumber) const;
|
const QVector<VBlockImageInfo2> *findImageInfoByBlock(int p_blockNumber) const;
|
||||||
|
|
||||||
const QPixmap *findImage(const QString &p_name) const;
|
const QPixmap *findImage(const QString &p_name) const;
|
||||||
|
|
||||||
@ -39,7 +39,9 @@ private:
|
|||||||
QHash<QString, QPixmap> m_images;
|
QHash<QString, QPixmap> m_images;
|
||||||
|
|
||||||
// Image info of all the blocks with image.
|
// Image info of all the blocks with image.
|
||||||
QHash<int, VBlockImageInfo2> m_blocksInfo;
|
// One block may contain multiple inline images or only one block image.
|
||||||
|
// If there are multiple inline images, they are sorted by the start position.
|
||||||
|
QHash<int, QVector<VBlockImageInfo2>> m_blocksInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VIMAGERESOURCEMANAGER2_H
|
#endif // VIMAGERESOURCEMANAGER2_H
|
||||||
|
@ -226,11 +226,6 @@ void VPreviewManager::updateBlockImageInfo(const QVector<ImageLinkInfo> &p_image
|
|||||||
for (int i = 0; i < p_imageLinks.size(); ++i) {
|
for (int i = 0; i < p_imageLinks.size(); ++i) {
|
||||||
const ImageLinkInfo &link = p_imageLinks[i];
|
const ImageLinkInfo &link = p_imageLinks[i];
|
||||||
|
|
||||||
// Skip inline images.
|
|
||||||
if (!link.m_isBlock) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString name = imageResourceName(link);
|
QString name = imageResourceName(link);
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
@ -240,7 +235,9 @@ void VPreviewManager::updateBlockImageInfo(const QVector<ImageLinkInfo> &p_image
|
|||||||
name,
|
name,
|
||||||
link.m_startPos - link.m_blockPos,
|
link.m_startPos - link.m_blockPos,
|
||||||
link.m_endPos - link.m_blockPos,
|
link.m_endPos - link.m_blockPos,
|
||||||
link.m_padding);
|
link.m_padding,
|
||||||
|
!link.m_isBlock);
|
||||||
|
|
||||||
blockInfos.push_back(info);
|
blockInfos.push_back(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include "vimageresourcemanager2.h"
|
#include "vimageresourcemanager2.h"
|
||||||
#include "vtextedit.h"
|
#include "vtextedit.h"
|
||||||
|
|
||||||
|
#define MARKER_THICKNESS 2
|
||||||
|
#define MAX_INLINE_IMAGE_HEIGHT 400
|
||||||
|
|
||||||
VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc,
|
VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc,
|
||||||
VImageResourceManager2 *p_imageMgr)
|
VImageResourceManager2 *p_imageMgr)
|
||||||
@ -223,7 +225,9 @@ void VTextDocumentLayout::draw(QPainter *p_painter, const PaintContext &p_contex
|
|||||||
selections,
|
selections,
|
||||||
p_context.clip.isValid() ? p_context.clip : QRectF());
|
p_context.clip.isValid() ? p_context.clip : QRectF());
|
||||||
|
|
||||||
drawBlockImage(p_painter, block, offset);
|
drawImages(p_painter, block, offset);
|
||||||
|
|
||||||
|
drawMarkers(p_painter, block, offset);
|
||||||
|
|
||||||
// Draw the cursor.
|
// Draw the cursor.
|
||||||
int blpos = block.position();
|
int blpos = block.position();
|
||||||
@ -502,8 +506,6 @@ void VTextDocumentLayout::layoutBlock(const QTextBlock &p_block)
|
|||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
Q_ASSERT(m_margin == doc->documentMargin());
|
Q_ASSERT(m_margin == doc->documentMargin());
|
||||||
|
|
||||||
// The height (y) of the next line.
|
|
||||||
qreal height = 0;
|
|
||||||
QTextLayout *tl = p_block.layout();
|
QTextLayout *tl = p_block.layout();
|
||||||
QTextOption option = doc->defaultTextOption();
|
QTextOption option = doc->defaultTextOption();
|
||||||
tl->setTextOption(option);
|
tl->setTextOption(option);
|
||||||
@ -521,40 +523,126 @@ void VTextDocumentLayout::layoutBlock(const QTextBlock &p_block)
|
|||||||
|
|
||||||
availableWidth -= (2 * m_margin + extraMargin + m_cursorMargin + m_cursorWidth);
|
availableWidth -= (2 * m_margin + extraMargin + m_cursorMargin + m_cursorWidth);
|
||||||
|
|
||||||
tl->beginLayout();
|
QVector<Marker> markers;
|
||||||
|
QVector<ImagePaintInfo> images;
|
||||||
|
|
||||||
while (true) {
|
layoutLines(p_block, tl, markers, images, availableWidth, 0);
|
||||||
QTextLine line = tl->createLine();
|
|
||||||
if (!line.isValid()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
line.setLeadingIncluded(true);
|
|
||||||
line.setLineWidth(availableWidth);
|
|
||||||
height += m_lineLeading;
|
|
||||||
line.setPosition(QPointF(m_margin, height));
|
|
||||||
height += line.height();
|
|
||||||
}
|
|
||||||
|
|
||||||
tl->endLayout();
|
|
||||||
|
|
||||||
// Set this block's line count to its layout's line count.
|
// Set this block's line count to its layout's line count.
|
||||||
// That is one block may occupy multiple visual lines.
|
// That is one block may occupy multiple visual lines.
|
||||||
const_cast<QTextBlock&>(p_block).setLineCount(p_block.isVisible() ? tl->lineCount() : 0);
|
const_cast<QTextBlock&>(p_block).setLineCount(p_block.isVisible() ? tl->lineCount() : 0);
|
||||||
|
|
||||||
// Update the info about this block.
|
// Update the info about this block.
|
||||||
finishBlockLayout(p_block);
|
finishBlockLayout(p_block, markers, images);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block)
|
qreal VTextDocumentLayout::layoutLines(const QTextBlock &p_block,
|
||||||
|
QTextLayout *p_tl,
|
||||||
|
QVector<Marker> &p_markers,
|
||||||
|
QVector<ImagePaintInfo> &p_images,
|
||||||
|
qreal p_availableWidth,
|
||||||
|
qreal p_height)
|
||||||
|
{
|
||||||
|
// Handle block inline image.
|
||||||
|
bool hasInlineImages = false;
|
||||||
|
const QVector<VBlockImageInfo2> *info = NULL;
|
||||||
|
if (m_blockImageEnabled) {
|
||||||
|
info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
|
||||||
|
|
||||||
|
if (info
|
||||||
|
&& !info->isEmpty()
|
||||||
|
&& info->first().m_inlineImage) {
|
||||||
|
hasInlineImages = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p_tl->beginLayout();
|
||||||
|
|
||||||
|
int imgIdx = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
QTextLine line = p_tl->createLine();
|
||||||
|
if (!line.isValid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line.setLeadingIncluded(true);
|
||||||
|
line.setLineWidth(p_availableWidth);
|
||||||
|
p_height += m_lineLeading;
|
||||||
|
|
||||||
|
if (hasInlineImages) {
|
||||||
|
QVector<const VBlockImageInfo2 *> images;
|
||||||
|
QVector<QPair<qreal, qreal>> imageRange;
|
||||||
|
qreal imgHeight = fetchInlineImagesForOneLine(*info,
|
||||||
|
&line,
|
||||||
|
m_margin,
|
||||||
|
imgIdx,
|
||||||
|
images,
|
||||||
|
imageRange);
|
||||||
|
|
||||||
|
for (int i = 0; i < images.size(); ++i) {
|
||||||
|
layoutInlineImage(images[i],
|
||||||
|
p_height,
|
||||||
|
imgHeight,
|
||||||
|
imageRange[i].first,
|
||||||
|
imageRange[i].second,
|
||||||
|
p_markers,
|
||||||
|
p_images);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!images.isEmpty()) {
|
||||||
|
p_height += imgHeight + MARKER_THICKNESS + MARKER_THICKNESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line.setPosition(QPointF(m_margin, p_height));
|
||||||
|
p_height += line.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
p_tl->endLayout();
|
||||||
|
|
||||||
|
return p_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VTextDocumentLayout::layoutInlineImage(const VBlockImageInfo2 *p_info,
|
||||||
|
qreal p_heightInBlock,
|
||||||
|
qreal p_imageSpaceHeight,
|
||||||
|
qreal p_xStart,
|
||||||
|
qreal p_xEnd,
|
||||||
|
QVector<Marker> &p_markers,
|
||||||
|
QVector<ImagePaintInfo> &p_images)
|
||||||
|
{
|
||||||
|
Marker mk;
|
||||||
|
qreal mky = p_imageSpaceHeight + p_heightInBlock + MARKER_THICKNESS;
|
||||||
|
mk.m_start = QPointF(p_xStart, mky);
|
||||||
|
mk.m_end = QPointF(p_xEnd, mky);
|
||||||
|
p_markers.append(mk);
|
||||||
|
|
||||||
|
if (p_info) {
|
||||||
|
QSize size = p_info->m_imageSize;
|
||||||
|
scaleSize(size, p_xEnd - p_xStart, p_imageSpaceHeight);
|
||||||
|
|
||||||
|
ImagePaintInfo ipi;
|
||||||
|
ipi.m_name = p_info->m_imageName;
|
||||||
|
ipi.m_rect = QRectF(QPointF(p_xStart,
|
||||||
|
p_heightInBlock + p_imageSpaceHeight - size.height()),
|
||||||
|
size);
|
||||||
|
p_images.append(ipi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block,
|
||||||
|
const QVector<Marker> &p_markers,
|
||||||
|
const QVector<ImagePaintInfo> &p_images)
|
||||||
{
|
{
|
||||||
// Update rect and offset.
|
// Update rect and offset.
|
||||||
Q_ASSERT(p_block.isValid());
|
Q_ASSERT(p_block.isValid());
|
||||||
int num = p_block.blockNumber();
|
int num = p_block.blockNumber();
|
||||||
Q_ASSERT(m_blocks.size() > num);
|
Q_ASSERT(m_blocks.size() > num);
|
||||||
|
ImagePaintInfo ipi;
|
||||||
BlockInfo &info = m_blocks[num];
|
BlockInfo &info = m_blocks[num];
|
||||||
info.reset();
|
info.reset();
|
||||||
info.m_rect = blockRectFromTextLayout(p_block);
|
info.m_rect = blockRectFromTextLayout(p_block, &ipi);
|
||||||
Q_ASSERT(!info.m_rect.isNull());
|
Q_ASSERT(!info.m_rect.isNull());
|
||||||
int pre = previousValidBlockNumber(num);
|
int pre = previousValidBlockNumber(num);
|
||||||
if (pre == -1) {
|
if (pre == -1) {
|
||||||
@ -563,6 +651,30 @@ void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block)
|
|||||||
info.m_offset = m_blocks[pre].bottom();
|
info.m_offset = m_blocks[pre].bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasImage = false;
|
||||||
|
if (ipi.isValid()) {
|
||||||
|
Q_ASSERT(p_markers.isEmpty());
|
||||||
|
Q_ASSERT(p_images.isEmpty());
|
||||||
|
info.m_images.append(ipi);
|
||||||
|
hasImage = true;
|
||||||
|
} else if (!p_markers.isEmpty()) {
|
||||||
|
// Q_ASSERT(!p_images.isEmpty());
|
||||||
|
info.m_markers = p_markers;
|
||||||
|
info.m_images = p_images;
|
||||||
|
hasImage = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add vertical marker.
|
||||||
|
if (hasImage) {
|
||||||
|
// Fill the marker.
|
||||||
|
// Will be adjusted using offset.
|
||||||
|
Marker mk;
|
||||||
|
mk.m_start = QPointF(-1, 0);
|
||||||
|
mk.m_end = QPointF(-1, info.m_rect.height());
|
||||||
|
|
||||||
|
info.m_markers.append(mk);
|
||||||
|
}
|
||||||
|
|
||||||
if (info.hasOffset()) {
|
if (info.hasOffset()) {
|
||||||
fillOffsetFrom(num);
|
fillOffsetFrom(num);
|
||||||
}
|
}
|
||||||
@ -622,8 +734,13 @@ int VTextDocumentLayout::cursorWidth() const
|
|||||||
return m_cursorWidth;
|
return m_cursorWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block)
|
QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block,
|
||||||
|
ImagePaintInfo *p_image)
|
||||||
{
|
{
|
||||||
|
if (p_image) {
|
||||||
|
*p_image = ImagePaintInfo();
|
||||||
|
}
|
||||||
|
|
||||||
QTextLayout *tl = p_block.layout();
|
QTextLayout *tl = p_block.layout();
|
||||||
if (tl->lineCount() < 1) {
|
if (tl->lineCount() < 1) {
|
||||||
return QRectF();
|
return QRectF();
|
||||||
@ -637,19 +754,31 @@ QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block)
|
|||||||
br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
|
br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle block image.
|
// Handle block non-inline image.
|
||||||
if (m_blockImageEnabled) {
|
if (m_blockImageEnabled) {
|
||||||
const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
|
const QVector<VBlockImageInfo2> *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
|
||||||
if (info && !info->m_imageSize.isNull()) {
|
if (info && info->size() == 1) {
|
||||||
|
const VBlockImageInfo2& img = info->first();
|
||||||
|
if (!img.m_inlineImage && !img.m_imageSize.isNull()) {
|
||||||
int maximumWidth = tlRect.width();
|
int maximumWidth = tlRect.width();
|
||||||
int padding;
|
int padding;
|
||||||
QSize size;
|
QSize size;
|
||||||
adjustImagePaddingAndSize(info, maximumWidth, padding, size);
|
adjustImagePaddingAndSize(&img, maximumWidth, padding, size);
|
||||||
|
|
||||||
|
if (p_image) {
|
||||||
|
p_image->m_name = img.m_imageName;
|
||||||
|
p_image->m_rect = QRectF(padding + m_margin,
|
||||||
|
br.height() + m_lineLeading,
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
}
|
||||||
|
|
||||||
int dw = padding + size.width() + m_margin - br.width();
|
int dw = padding + size.width() + m_margin - br.width();
|
||||||
int dh = size.height() + m_lineLeading;
|
int dh = size.height() + m_lineLeading;
|
||||||
br.adjust(0, 0, dw > 0 ? dw : 0, dh);
|
br.adjust(0, 0, dw > 0 ? dw : 0, dh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
br.adjust(0, 0, m_margin + m_cursorWidth, 0);
|
br.adjust(0, 0, m_margin + m_cursorWidth, 0);
|
||||||
|
|
||||||
@ -729,41 +858,53 @@ void VTextDocumentLayout::adjustImagePaddingAndSize(const VBlockImageInfo2 *p_in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VTextDocumentLayout::drawBlockImage(QPainter *p_painter,
|
void VTextDocumentLayout::drawImages(QPainter *p_painter,
|
||||||
const QTextBlock &p_block,
|
const QTextBlock &p_block,
|
||||||
const QPointF &p_offset)
|
const QPointF &p_offset)
|
||||||
{
|
{
|
||||||
if (!m_blockImageEnabled) {
|
if (m_blocks.size() <= p_block.blockNumber()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
|
const QVector<ImagePaintInfo> &images = m_blocks[p_block.blockNumber()].m_images;
|
||||||
if (!info || info->m_imageSize.isNull()) {
|
if (images.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QPixmap *image = m_imageMgr->findImage(info->m_imageName);
|
for (auto const & img : images) {
|
||||||
|
const QPixmap *image = m_imageMgr->findImage(img.m_name);
|
||||||
Q_ASSERT(image);
|
Q_ASSERT(image);
|
||||||
|
QRect targetRect = img.m_rect.adjusted(p_offset.x(),
|
||||||
// Draw block image.
|
p_offset.y(),
|
||||||
QTextLayout *tl = p_block.layout();
|
p_offset.x(),
|
||||||
QRectF tlRect = tl->boundingRect();
|
p_offset.y()).toRect();
|
||||||
int maximumWidth = tlRect.width();
|
|
||||||
int padding;
|
|
||||||
QSize size;
|
|
||||||
adjustImagePaddingAndSize(info, maximumWidth, padding, size);
|
|
||||||
QRect targetRect(p_offset.x() + padding,
|
|
||||||
p_offset.y() + tlRect.height() + m_lineLeading,
|
|
||||||
size.width(),
|
|
||||||
size.height());
|
|
||||||
|
|
||||||
p_painter->drawPixmap(targetRect, *image);
|
p_painter->drawPixmap(targetRect, *image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VTextDocumentLayout::drawMarkers(QPainter *p_painter,
|
||||||
|
const QTextBlock &p_block,
|
||||||
|
const QPointF &p_offset)
|
||||||
|
{
|
||||||
|
if (m_blocks.size() <= p_block.blockNumber()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<Marker> &markers = m_blocks[p_block.blockNumber()].m_markers;
|
||||||
|
if (markers.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw a thin line to link them.
|
|
||||||
QPen oldPen = p_painter->pen();
|
QPen oldPen = p_painter->pen();
|
||||||
QPen newPen(m_imageLineColor, 2, Qt::DashLine);
|
QPen newPen(m_imageLineColor, MARKER_THICKNESS, Qt::DashLine);
|
||||||
p_painter->setPen(newPen);
|
p_painter->setPen(newPen);
|
||||||
p_painter->drawLine(QPointF(2, p_offset.y()), QPointF(2, targetRect.bottom()));
|
|
||||||
|
for (auto const & mk : markers) {
|
||||||
|
p_painter->drawLine(mk.m_start + p_offset, mk.m_end + p_offset);
|
||||||
|
}
|
||||||
|
|
||||||
p_painter->setPen(oldPen);
|
p_painter->setPen(oldPen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -810,3 +951,101 @@ void VTextDocumentLayout::relayout(const QSet<int> &p_blocks)
|
|||||||
|
|
||||||
updateDocumentSize();
|
updateDocumentSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qreal VTextDocumentLayout::fetchInlineImagesForOneLine(const QVector<VBlockImageInfo2> &p_info,
|
||||||
|
const QTextLine *p_line,
|
||||||
|
qreal p_margin,
|
||||||
|
int &p_index,
|
||||||
|
QVector<const VBlockImageInfo2 *> &p_images,
|
||||||
|
QVector<QPair<qreal, qreal>> &p_imageRange)
|
||||||
|
{
|
||||||
|
qreal maxHeight = 0;
|
||||||
|
int start = p_line->textStart();
|
||||||
|
int end = p_line->textLength() + start;
|
||||||
|
|
||||||
|
for (int i = 0; i < p_info.size(); ++i) {
|
||||||
|
const VBlockImageInfo2 &img = p_info[i];
|
||||||
|
Q_ASSERT(img.m_inlineImage);
|
||||||
|
|
||||||
|
if (img.m_imageSize.isNull()) {
|
||||||
|
p_index = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.m_startPos >= start && img.m_startPos < end) {
|
||||||
|
// Start of a new image.
|
||||||
|
qreal startX = p_line->cursorToX(img.m_startPos) + p_margin;
|
||||||
|
qreal endX;
|
||||||
|
if (img.m_endPos <= end) {
|
||||||
|
// End an image.
|
||||||
|
endX = p_line->cursorToX(img.m_endPos) + p_margin;
|
||||||
|
p_images.append(&img);
|
||||||
|
p_imageRange.append(QPair<qreal, qreal>(startX, endX));
|
||||||
|
|
||||||
|
QSize size = img.m_imageSize;
|
||||||
|
scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
|
||||||
|
if (size.height() > maxHeight) {
|
||||||
|
maxHeight = size.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image i has been drawn.
|
||||||
|
p_index = i + 1;
|
||||||
|
} else {
|
||||||
|
// This image cross the line.
|
||||||
|
endX = p_line->x() + p_line->width() + p_margin;
|
||||||
|
if (end - img.m_startPos >= ((img.m_endPos - img.m_startPos) >> 1)) {
|
||||||
|
// Put image at this side.
|
||||||
|
p_images.append(&img);
|
||||||
|
p_imageRange.append(QPair<qreal, qreal>(startX, endX));
|
||||||
|
|
||||||
|
QSize size = img.m_imageSize;
|
||||||
|
scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
|
||||||
|
if (size.height() > maxHeight) {
|
||||||
|
maxHeight = size.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image i has been drawn.
|
||||||
|
p_index = i + 1;
|
||||||
|
} else {
|
||||||
|
// Just put a marker here.
|
||||||
|
p_images.append(NULL);
|
||||||
|
p_imageRange.append(QPair<qreal, qreal>(startX, endX));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (img.m_endPos > start && img.m_startPos < start) {
|
||||||
|
qreal startX = p_line->x() + p_margin;
|
||||||
|
qreal endX = img.m_endPos > end ? p_line->x() + p_line->width()
|
||||||
|
: p_line->cursorToX(img.m_endPos);
|
||||||
|
if (p_index <= i) {
|
||||||
|
// Image i has not been drawn. Draw it here.
|
||||||
|
p_images.append(&img);
|
||||||
|
p_imageRange.append(QPair<qreal, qreal>(startX, endX));
|
||||||
|
|
||||||
|
QSize size = img.m_imageSize;
|
||||||
|
scaleSize(size, endX - startX, MAX_INLINE_IMAGE_HEIGHT);
|
||||||
|
if (size.height() > maxHeight) {
|
||||||
|
maxHeight = size.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image i has been drawn.
|
||||||
|
p_index = i + 1;
|
||||||
|
} else {
|
||||||
|
// Image i has been drawn. Just put a marker here.
|
||||||
|
p_images.append(NULL);
|
||||||
|
p_imageRange.append(QPair<qreal, qreal>(startX, endX));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.m_endPos >= end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (img.m_endPos <= start) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxHeight;
|
||||||
|
}
|
||||||
|
@ -57,6 +57,27 @@ protected:
|
|||||||
void documentChanged(int p_from, int p_charsRemoved, int p_charsAdded) Q_DECL_OVERRIDE;
|
void documentChanged(int p_from, int p_charsRemoved, int p_charsAdded) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Denote the start and end position of a marker line.
|
||||||
|
struct Marker
|
||||||
|
{
|
||||||
|
QPointF m_start;
|
||||||
|
QPointF m_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImagePaintInfo
|
||||||
|
{
|
||||||
|
// The rect to draw the image.
|
||||||
|
QRectF m_rect;
|
||||||
|
|
||||||
|
// Name of the image.
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
return !m_name.isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct BlockInfo
|
struct BlockInfo
|
||||||
{
|
{
|
||||||
BlockInfo()
|
BlockInfo()
|
||||||
@ -68,6 +89,8 @@ private:
|
|||||||
{
|
{
|
||||||
m_offset = -1;
|
m_offset = -1;
|
||||||
m_rect = QRectF();
|
m_rect = QRectF();
|
||||||
|
m_markers.clear();
|
||||||
|
m_images.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasOffset() const
|
bool hasOffset() const
|
||||||
@ -94,10 +117,50 @@ private:
|
|||||||
// The bounding rect of this block, including the margins.
|
// The bounding rect of this block, including the margins.
|
||||||
// Null for invalid.
|
// Null for invalid.
|
||||||
QRectF m_rect;
|
QRectF m_rect;
|
||||||
|
|
||||||
|
// Markers to draw for this block.
|
||||||
|
// Y is the offset within this block.
|
||||||
|
QVector<Marker> m_markers;
|
||||||
|
|
||||||
|
// Images to draw for this block.
|
||||||
|
// Y is the offset within this block.
|
||||||
|
QVector<ImagePaintInfo> m_images;
|
||||||
};
|
};
|
||||||
|
|
||||||
void layoutBlock(const QTextBlock &p_block);
|
void layoutBlock(const QTextBlock &p_block);
|
||||||
|
|
||||||
|
// Returns the total height of this block after layouting lines and inline
|
||||||
|
// images.
|
||||||
|
qreal layoutLines(const QTextBlock &p_block,
|
||||||
|
QTextLayout *p_tl,
|
||||||
|
QVector<Marker> &p_markers,
|
||||||
|
QVector<ImagePaintInfo> &p_images,
|
||||||
|
qreal p_availableWidth,
|
||||||
|
qreal p_height);
|
||||||
|
|
||||||
|
// Layout inline image in a line.
|
||||||
|
// @p_info: if NULL, means just layout a marker.
|
||||||
|
// Returns the image height.
|
||||||
|
void layoutInlineImage(const VBlockImageInfo2 *p_info,
|
||||||
|
qreal p_heightInBlock,
|
||||||
|
qreal p_imageSpaceHeight,
|
||||||
|
qreal p_xStart,
|
||||||
|
qreal p_xEnd,
|
||||||
|
QVector<Marker> &p_markers,
|
||||||
|
QVector<ImagePaintInfo> &p_images);
|
||||||
|
|
||||||
|
// Get inline images belonging to @p_line from @p_info.
|
||||||
|
// @p_index: image [0, p_index) has been drawn.
|
||||||
|
// @p_images: contains all images and markers (NULL element indicates it
|
||||||
|
// is just a placeholder for the marker.
|
||||||
|
// Returns the maximum height of the images.
|
||||||
|
qreal fetchInlineImagesForOneLine(const QVector<VBlockImageInfo2> &p_info,
|
||||||
|
const QTextLine *p_line,
|
||||||
|
qreal p_margin,
|
||||||
|
int &p_index,
|
||||||
|
QVector<const VBlockImageInfo2 *> &p_images,
|
||||||
|
QVector<QPair<qreal, qreal>> &p_imageRange);
|
||||||
|
|
||||||
// Clear the layout of @p_block.
|
// Clear the layout of @p_block.
|
||||||
// Also clear all the offset behind this block.
|
// Also clear all the offset behind this block.
|
||||||
void clearBlockLayout(QTextBlock &p_block);
|
void clearBlockLayout(QTextBlock &p_block);
|
||||||
@ -115,7 +178,9 @@ private:
|
|||||||
|
|
||||||
bool validateBlocks() const;
|
bool validateBlocks() const;
|
||||||
|
|
||||||
void finishBlockLayout(const QTextBlock &p_block);
|
void finishBlockLayout(const QTextBlock &p_block,
|
||||||
|
const QVector<Marker> &p_markers,
|
||||||
|
const QVector<ImagePaintInfo> &p_images);
|
||||||
|
|
||||||
int previousValidBlockNumber(int p_number) const;
|
int previousValidBlockNumber(int p_number) const;
|
||||||
|
|
||||||
@ -136,8 +201,11 @@ private:
|
|||||||
void blockRangeFromRectBS(const QRectF &p_rect, int &p_first, int &p_last) const;
|
void blockRangeFromRectBS(const QRectF &p_rect, int &p_first, int &p_last) const;
|
||||||
|
|
||||||
// Return a rect from the layout.
|
// Return a rect from the layout.
|
||||||
|
// If @p_imageRect is not NULL and there is block image for this block, it will
|
||||||
|
// be set to the rect of that image.
|
||||||
// Return a null rect if @p_block has not been layouted.
|
// Return a null rect if @p_block has not been layouted.
|
||||||
QRectF blockRectFromTextLayout(const QTextBlock &p_block);
|
QRectF blockRectFromTextLayout(const QTextBlock &p_block,
|
||||||
|
ImagePaintInfo *p_image = NULL);
|
||||||
|
|
||||||
// Update document size when only block @p_blockNumber is changed and the height
|
// Update document size when only block @p_blockNumber is changed and the height
|
||||||
// remain the same.
|
// remain the same.
|
||||||
@ -150,10 +218,16 @@ private:
|
|||||||
|
|
||||||
// Draw images of block @p_block.
|
// Draw images of block @p_block.
|
||||||
// @p_offset: the offset for the drawing of the block.
|
// @p_offset: the offset for the drawing of the block.
|
||||||
void drawBlockImage(QPainter *p_painter,
|
void drawImages(QPainter *p_painter,
|
||||||
const QTextBlock &p_block,
|
const QTextBlock &p_block,
|
||||||
const QPointF &p_offset);
|
const QPointF &p_offset);
|
||||||
|
|
||||||
|
void drawMarkers(QPainter *p_painter,
|
||||||
|
const QTextBlock &p_block,
|
||||||
|
const QPointF &p_offset);
|
||||||
|
|
||||||
|
void scaleSize(QSize &p_size, int p_width, int p_height);
|
||||||
|
|
||||||
// Document margin on left/right/bottom.
|
// Document margin on left/right/bottom.
|
||||||
qreal m_margin;
|
qreal m_margin;
|
||||||
|
|
||||||
@ -201,4 +275,10 @@ inline void VTextDocumentLayout::setImageLineColor(const QColor &p_color)
|
|||||||
m_imageLineColor = p_color;
|
m_imageLineColor = p_color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void VTextDocumentLayout::scaleSize(QSize &p_size, int p_width, int p_height)
|
||||||
|
{
|
||||||
|
if (p_size.width() > p_width || p_size.height() > p_height) {
|
||||||
|
p_size.scale(p_width, p_height, Qt::KeepAspectRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // VTEXTDOCUMENTLAYOUT_H
|
#endif // VTEXTDOCUMENTLAYOUT_H
|
||||||
|
@ -50,6 +50,19 @@ public:
|
|||||||
&& m_imageSize == p_other.m_imageSize;
|
&& m_imageSize == p_other.m_imageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator<(const VBlockImageInfo2 &p_other) const
|
||||||
|
{
|
||||||
|
if (m_blockNumber < p_other.m_blockNumber) {
|
||||||
|
return true;
|
||||||
|
} else if (m_blockNumber > p_other.m_blockNumber) {
|
||||||
|
return false;
|
||||||
|
} else if (m_startPos < p_other.m_startPos) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString toString() const
|
QString toString() const
|
||||||
{
|
{
|
||||||
return QString("VBlockImageInfo2 block %1 start %2 end %3 padding %4 "
|
return QString("VBlockImageInfo2 block %1 start %2 end %3 padding %4 "
|
||||||
|
Loading…
x
Reference in New Issue
Block a user