📄 qtextdocumentlayout.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qtextdocumentlayout_p.h"#include "qtextimagehandler_p.h"#include "qtexttable.h"#include "qtextlist.h"#include "qabstracttextdocumentlayout_p.h"#include <qpainter.h>#include <qrect.h>#include <qpalette.h>#include <qdebug.h>#include <qvarlengtharray.h>#include <limits.h>#include <qstyle.h>#include <qbasictimer.h>// #define LAYOUT_DEBUG#ifdef LAYOUT_DEBUG#define LDEBUG qDebug()#define INC_INDENT debug_indent += " "#define DEC_INDENT debug_indent = debug_indent.left(debug_indent.length()-2)#else#define LDEBUG if(0) qDebug()#define INC_INDENT do {} while(0)#define DEC_INDENT do {} while(0)#endif// ################ should probably add frameFormatChange notification!struct QLayoutStruct;class QTextFrameData : public QTextFrameLayoutData{public: QTextFrameData() : minimumWidth(0), maximumWidth(INT_MAX), currentLayoutStruct(0), sizeDirty(true), layoutDirty(true) {} // relative to parent frame QPointF position; QSizeF size; // contents starts at (margin+border/margin+border) qreal margin; qreal border; qreal padding; // contents width includes padding (as we need to treat this on a per cell basis for tables) qreal contentsWidth; qreal contentsHeight; qreal minimumWidth; qreal maximumWidth; QTextFrameFormat::Position flow_position; QLayoutStruct *currentLayoutStruct; bool sizeDirty; bool layoutDirty; QList<QPointer<QTextFrame> > floats;};struct QLayoutStruct { QLayoutStruct() : contentsWidth(0), minimumWidth(0), maximumWidth(INT_MAX), fullLayout(false), pageHeight(0.0), pageBottom(0.0), pageMargin(0.0) {} QTextFrame *frame; qreal x_left; qreal x_right; qreal y; qreal contentsWidth; qreal minimumWidth; qreal maximumWidth; bool fullLayout; QList<QTextFrame *> pendingFloats; qreal pageHeight; qreal pageBottom; qreal pageMargin; QRectF updateRect; inline void newPage() { pageBottom += pageHeight; y = pageBottom - pageHeight + 2 * pageMargin; }};class QTextTableData : public QTextFrameData{public: inline QTextTableData() : cellSpacing(0), cellPadding(0) {} qreal cellSpacing, cellPadding; QVector<qreal> minWidths; QVector<qreal> maxWidths; QVector<qreal> widths; QVector<qreal> heights; QVector<qreal> columnPositions; QVector<qreal> rowPositions; // rows that appear at the top of a page after a page break QVector<int> rowPageBreaks; QVector<qreal> rowPositionsBeforePageBreak; inline qreal cellWidth(int column, int colspan) const { return columnPositions.at(column + colspan - 1) + widths.at(column + colspan - 1) - columnPositions.at(column) - 2 * cellPadding; } inline void calcRowPosition(int row) { if (row > 0) rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + border + cellSpacing + border; } QRectF cellRect(const QTextTableCell &cell) const; inline QPointF cellPosition(int row, int col) const { return QPointF(columnPositions.at(col) + cellPadding, rowPositions.at(row) + cellPadding); } inline QPointF cellPosition(const QTextTableCell &cell) const { return cellPosition(cell.row(), cell.column()); } void updateSize();};static QTextFrameData *createData(QTextFrame *f){ QTextFrameData *data; if (qobject_cast<QTextTable *>(f)) data = new QTextTableData; else data = new QTextFrameData; f->setLayoutData(data); return data;}static inline QTextFrameData *data(QTextFrame *f){ QTextFrameData *data = static_cast<QTextFrameData *>(f->layoutData()); if (!data) data = createData(f); return data;}void QTextTableData::updateSize(){ const qreal effectiveMargin = this->margin + border + padding; qreal height = contentsHeight == -1 ? rowPositions.last() + heights.last() + padding + border + cellSpacing + effectiveMargin : contentsHeight + 2*effectiveMargin; size = QSizeF(contentsWidth + 2*effectiveMargin, height);}QRectF QTextTableData::cellRect(const QTextTableCell &cell) const{ const int row = cell.row(); const int rowSpan = cell.rowSpan(); const int column = cell.column(); const int colSpan = cell.columnSpan(); return QRectF(columnPositions.at(column), rowPositions.at(row), columnPositions.at(column + colSpan - 1) + widths.at(column + colSpan - 1) - columnPositions.at(column), rowPositions.at(row + rowSpan - 1) + heights.at(row + rowSpan - 1) - rowPositions.at(row));}static inline bool isEmptyBlockBeforeTable(const QTextBlock &block, const QTextFrame::Iterator &nextIt){ return !nextIt.atEnd() && qobject_cast<QTextTable *>(nextIt.currentFrame()) && block.isValid() && block.length() == 1 && !block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth) && nextIt.currentFrame()->firstPosition() == block.position() + 1 ;}static inline bool isEmptyBlockBeforeTable(QTextFrame::Iterator it){ QTextFrame::Iterator next = it; ++next; return it.currentFrame() == 0 && isEmptyBlockBeforeTable(it.currentBlock(), next);}static inline bool isEmptyBlockAfterTable(const QTextBlock &block, const QTextFrame *lastFrame){ return qobject_cast<const QTextTable *>(lastFrame) && block.isValid() && block.length() == 1 && lastFrame->lastPosition() == block.position() - 1 ;}/*Optimisation strategies:HTML layout:* Distinguish between normal and special flow. For normal flow the condition: y1 > y2 holds for all blocks with b1.key() > b2.key().* Special flow is: floats, table cells* Normal flow within table cells. Tables (not cells) are part of the normal flow.* If blocks grows/shrinks in height and extends over whole page width at the end, move following blocks.* If height doesn't change, no need to do anythingTable cells:* If minWidth of cell changes, recalculate table width, relayout if needed.* What about maxWidth when doing auto layout?Floats:* need fixed or proportional width, otherwise don't float!* On width/height change relayout surrounding paragraphs.Document width change:* full relayout neededFloat handling:* Floats are specified by a special format object.* currently only floating images are implemented.*//* On the table layouting: +---[ table border ]------------------------- | [ cell spacing ] | +------[ cell border ]-----+ +-------- | | | | | | | | | | | rowPositions[i] and columnPositions[i] point at the cell content position. So for example the left border is drawn at x = columnPositions[i] - fd->border and similar for y.*/enum { TextIndentValue = 40};struct QCheckPoint{ qreal y; int positionInFrame; qreal minimumWidth; qreal maximumWidth; qreal contentsWidth;};Q_DECLARE_TYPEINFO(QCheckPoint, Q_PRIMITIVE_TYPE);static bool operator<(const QCheckPoint &checkPoint, qreal y){ return checkPoint.y < y;}static bool operator<(const QCheckPoint &checkPoint, int pos){ return checkPoint.positionInFrame < pos;}class QTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate{ Q_DECLARE_PUBLIC(QTextDocumentLayout)public:#if 0 struct Page { QTextBlock first; QTextBlock last; }; QList<Page> pages;#endif QTextDocumentLayoutPrivate() : blockTextFlags(0), wordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere), fixedColumnWidth(-1), tabStopWidth(80), // same default as in qtextengine.cpp currentLazyLayoutPosition(-1), lazyLayoutStepSize(1000), showLayoutProgress(true) { } bool pagedLayout; int blockTextFlags; QTextOption::WrapMode wordWrapMode;#ifdef LAYOUT_DEBUG mutable QString debug_indent;#endif int fixedColumnWidth; qreal tabStopWidth; mutable int currentLazyLayoutPosition; mutable int lazyLayoutStepSize; QBasicTimer layoutTimer; mutable QBasicTimer sizeChangedTimer; bool showLayoutProgress; qreal indent(QTextBlock bl) const; void drawFrame(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextFrame *f) const; void drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextFrame::Iterator it, QTextBlock *cursorBlockNeedingRepaint) const; void drawBlock(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextBlock bl) const; void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextBlock bl, const QTextCharFormat *selectionFormat) const; enum HitPoint { PointBefore, PointAfter, PointInside, PointExact }; HitPoint hitTest(QTextFrame *frame, const QPointF &point, int *position, QTextLayout **l) const; HitPoint hitTest(QTextFrame::Iterator it, HitPoint hit, const QPointF &p, int *position, QTextLayout **l) const; HitPoint hitTest(QTextTable *table, const QPointF &point, int *position, QTextLayout **l) const; HitPoint hitTest(QTextBlock bl, const QPointF &point, int *position, QTextLayout **l) const;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -