#ifndef VTEXTDOCUMENTLAYOUT_H #define VTEXTDOCUMENTLAYOUT_H #include #include #include #include #include "vconstants.h" class VImageResourceManager2; struct VPreviewedImageInfo; struct VPreviewInfo; struct QMapDummyValue { }; typedef QMap OrderedIntSet; class VTextDocumentLayout : public QAbstractTextDocumentLayout { Q_OBJECT public: VTextDocumentLayout(QTextDocument *p_doc, VImageResourceManager2 *p_imageMgr); void draw(QPainter *p_painter, const PaintContext &p_context) Q_DECL_OVERRIDE; int hitTest(const QPointF &p_point, Qt::HitTestAccuracy p_accuracy) const Q_DECL_OVERRIDE; int pageCount() const Q_DECL_OVERRIDE; QSizeF documentSize() const Q_DECL_OVERRIDE; QRectF frameBoundingRect(QTextFrame *p_frame) const Q_DECL_OVERRIDE; QRectF blockBoundingRect(const QTextBlock &p_block) const Q_DECL_OVERRIDE; void setCursorWidth(int p_width); int cursorWidth() const; void setLineLeading(qreal p_leading); qreal getLineLeading() const; // Return the block number which contains point @p_point. // If @p_point is at the border, returns the block below. int findBlockByPosition(const QPointF &p_point) const; void setImageWidthConstrainted(bool p_enabled); void setBlockImageEnabled(bool p_enabled); // Relayout all the blocks. void relayout(); // Relayout @p_blocks. void relayout(const OrderedIntSet &p_blocks); void setImageLineColor(const QColor &p_color); void setCursorBlockMode(CursorBlock p_mode); void setVirtualCursorBlockWidth(int p_width); void clearLastCursorBlockWidth(); void setHighlightCursorLineBlockEnabled(bool p_enabled); void setCursorLineBlockBg(const QColor &p_bg); void setCursorLineBlockNumber(int p_blockNumber); // Request update block by block number. void updateBlockByNumber(int p_blockNumber); signals: // Emit to update current cursor block width if m_cursorBlockMode is enabled. void cursorBlockWidthUpdated(int p_width); protected: void documentChanged(int p_from, int p_charsRemoved, int p_charsAdded) Q_DECL_OVERRIDE; 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 { BlockInfo() { reset(); } void reset() { m_offset = -1; m_rect = QRectF(); m_markers.clear(); m_images.clear(); } bool hasOffset() const { return m_offset > -1 && !m_rect.isNull(); } qreal top() const { Q_ASSERT(hasOffset()); return m_offset; } qreal bottom() const { Q_ASSERT(hasOffset()); return m_offset + m_rect.height(); } // Y offset of this block. // -1 for invalid. qreal m_offset; // The bounding rect of this block, including the margins. // Null for invalid. QRectF m_rect; // Markers to draw for this block. // Y is the offset within this block. QVector m_markers; // Images to draw for this block. // Y is the offset within this block. QVector m_images; }; 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 &p_markers, QVector &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 VPreviewedImageInfo *p_info, qreal p_heightInBlock, qreal p_imageSpaceHeight, qreal p_xStart, qreal p_xEnd, QVector &p_markers, QVector &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 &p_info, const QTextLine *p_line, qreal p_margin, int &p_index, QVector &p_images, QVector> &p_imageRange); // Clear the layout of @p_block. // Also clear all the offset behind this block. void clearBlockLayout(QTextBlock &p_block); // Clear the offset of all the blocks from @p_blockNumber. void clearOffsetFrom(int p_blockNumber); // Fill the offset filed from @p_blockNumber + 1. void fillOffsetFrom(int p_blockNumber); // Update block count to @p_count due to document change. // Maintain m_blocks. // @p_changeStartBlock is the block number of the start block in this change. void updateBlockCount(int p_count, int p_changeStartBlock); bool validateBlocks() const; void finishBlockLayout(const QTextBlock &p_block, const QVector &p_markers, const QVector &p_images); int previousValidBlockNumber(int p_number) const; int nextValidBlockNumber(int p_number) const; // Update block count and m_blocks size. void updateDocumentSize(); QVector formatRangeFromSelection(const QTextBlock &p_block, const QVector &p_selections) const; // Get the block range [first, last] by rect @p_rect. // @p_rect: a clip region in document coordinates. If null, returns all the blocks. // Return [-1, -1] if no valid block range found. void blockRangeFromRect(const QRectF &p_rect, int &p_first, int &p_last) const; // Binary search to get the block range [first, last] by @p_rect. void blockRangeFromRectBS(const QRectF &p_rect, int &p_first, int &p_last) const; // 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. QRectF blockRectFromTextLayout(const QTextBlock &p_block, ImagePaintInfo *p_image = NULL); // Update document size when only block @p_blockNumber is changed and the height // remain the same. void updateDocumentSizeWithOneBlockChanged(int p_blockNumber); void adjustImagePaddingAndSize(const VPreviewedImageInfo *p_info, int p_maximumWidth, int &p_padding, QSize &p_size) const; // Draw images of block @p_block. // @p_offset: the offset for the drawing of the block. void drawImages(QPainter *p_painter, const QTextBlock &p_block, 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); // Get text length in pixel. // @p_pos: position within the layout. int getTextWidthWithinTextLine(const QTextLayout *p_layout, int p_pos, int p_length); // Document margin on left/right/bottom. qreal m_margin; // Maximum width of the contents. qreal m_width; // The block number of the block which contains the m_width. int m_maximumWidthBlockNumber; // Height of all the document (all the blocks). qreal m_height; // Set the leading space of a line. qreal m_lineLeading; // Block count of the document. int m_blockCount; // Width of the cursor. int m_cursorWidth; // Right margin for cursor. qreal m_cursorMargin; QVector m_blocks; VImageResourceManager2 *m_imageMgr; bool m_blockImageEnabled; // Whether constraint the width of image to the width of the page. bool m_imageWidthConstrainted; // Color of the image line. QColor m_imageLineColor; // Draw cursor as block. CursorBlock m_cursorBlockMode; // Virtual cursor block: cursor block on no character. int m_virtualCursorBlockWidth; int m_lastCursorBlockWidth; // Whether highlight the block containing the cursor line. bool m_highlightCursorLineBlock; // The cursor line's block background color. QColor m_cursorLineBlockBg; // The block containing the cursor. int m_cursorLineBlockNumber; }; inline qreal VTextDocumentLayout::getLineLeading() const { return m_lineLeading; } inline void VTextDocumentLayout::setImageLineColor(const QColor &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); } } inline void VTextDocumentLayout::setCursorBlockMode(CursorBlock p_mode) { m_cursorBlockMode = p_mode; } inline void VTextDocumentLayout::setVirtualCursorBlockWidth(int p_width) { m_virtualCursorBlockWidth = p_width; } inline void VTextDocumentLayout::clearLastCursorBlockWidth() { m_lastCursorBlockWidth = -1; } inline void VTextDocumentLayout::setHighlightCursorLineBlockEnabled(bool p_enabled) { if (m_highlightCursorLineBlock != p_enabled) { m_highlightCursorLineBlock = p_enabled; if (!m_highlightCursorLineBlock) { int pre = m_cursorLineBlockNumber; m_cursorLineBlockNumber = -1; updateBlockByNumber(pre); } } } inline void VTextDocumentLayout::setCursorLineBlockBg(const QColor &p_bg) { if (p_bg != m_cursorLineBlockBg) { m_cursorLineBlockBg = p_bg; updateBlockByNumber(m_cursorLineBlockNumber); } } inline void VTextDocumentLayout::setCursorLineBlockNumber(int p_blockNumber) { if (p_blockNumber != m_cursorLineBlockNumber) { int pre = m_cursorLineBlockNumber; m_cursorLineBlockNumber = p_blockNumber; if (m_highlightCursorLineBlock) { updateBlockByNumber(pre); updateBlockByNumber(m_cursorLineBlockNumber); } } } #endif // VTEXTDOCUMENTLAYOUT_H