vtextdocumentlayout.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. #include "vtextdocumentlayout.h"
  2. #include <QTextDocument>
  3. #include <QTextBlock>
  4. #include <QTextFrame>
  5. #include <QTextLayout>
  6. #include <QPointF>
  7. #include <QFontMetrics>
  8. #include <QFont>
  9. #include <QPainter>
  10. #include <QDebug>
  11. #include "vimageresourcemanager2.h"
  12. #include "vtextedit.h"
  13. VTextDocumentLayout::VTextDocumentLayout(QTextDocument *p_doc,
  14. VImageResourceManager2 *p_imageMgr)
  15. : QAbstractTextDocumentLayout(p_doc),
  16. m_margin(p_doc->documentMargin()),
  17. m_width(0),
  18. m_maximumWidthBlockNumber(-1),
  19. m_height(0),
  20. m_lineLeading(0),
  21. m_blockCount(0),
  22. m_cursorWidth(1),
  23. m_cursorMargin(4),
  24. m_imageMgr(p_imageMgr),
  25. m_blockImageEnabled(false),
  26. m_imageWidthConstrainted(false)
  27. {
  28. }
  29. static void fillBackground(QPainter *p_painter,
  30. const QRectF &p_rect,
  31. QBrush p_brush,
  32. QRectF p_gradientRect = QRectF())
  33. {
  34. p_painter->save();
  35. if (p_brush.style() >= Qt::LinearGradientPattern
  36. && p_brush.style() <= Qt::ConicalGradientPattern) {
  37. if (!p_gradientRect.isNull()) {
  38. QTransform m = QTransform::fromTranslate(p_gradientRect.left(),
  39. p_gradientRect.top());
  40. m.scale(p_gradientRect.width(), p_gradientRect.height());
  41. p_brush.setTransform(m);
  42. const_cast<QGradient *>(p_brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
  43. }
  44. } else {
  45. p_painter->setBrushOrigin(p_rect.topLeft());
  46. }
  47. p_painter->fillRect(p_rect, p_brush);
  48. p_painter->restore();
  49. }
  50. void VTextDocumentLayout::blockRangeFromRect(const QRectF &p_rect,
  51. int &p_first,
  52. int &p_last) const
  53. {
  54. if (p_rect.isNull()) {
  55. p_first = 0;
  56. p_last = m_blocks.size() - 1;
  57. return;
  58. }
  59. p_first = -1;
  60. p_last = m_blocks.size() - 1;
  61. int y = p_rect.y();
  62. Q_ASSERT(document()->blockCount() == m_blocks.size());
  63. QTextBlock block = document()->firstBlock();
  64. while (block.isValid()) {
  65. const BlockInfo &info = m_blocks[block.blockNumber()];
  66. Q_ASSERT(info.hasOffset());
  67. if (info.top() == y
  68. || (info.top() < y && info.bottom() >= y)) {
  69. p_first = block.blockNumber();
  70. break;
  71. }
  72. block = block.next();
  73. }
  74. if (p_first == -1) {
  75. p_last = -1;
  76. return;
  77. }
  78. y += p_rect.height();
  79. while (block.isValid()) {
  80. const BlockInfo &info = m_blocks[block.blockNumber()];
  81. Q_ASSERT(info.hasOffset());
  82. if (info.bottom() > y) {
  83. p_last = block.blockNumber();
  84. break;
  85. }
  86. block = block.next();
  87. }
  88. }
  89. void VTextDocumentLayout::blockRangeFromRectBS(const QRectF &p_rect,
  90. int &p_first,
  91. int &p_last) const
  92. {
  93. if (p_rect.isNull()) {
  94. p_first = 0;
  95. p_last = m_blocks.size() - 1;
  96. return;
  97. }
  98. Q_ASSERT(document()->blockCount() == m_blocks.size());
  99. p_first = findBlockByPosition(p_rect.topLeft());
  100. if (p_first == -1) {
  101. p_last = -1;
  102. return;
  103. }
  104. int y = p_rect.bottom();
  105. QTextBlock block = document()->findBlockByNumber(p_first);
  106. if (m_blocks[p_first].top() == p_rect.top()
  107. && p_first > 0) {
  108. --p_first;
  109. }
  110. p_last = m_blocks.size() - 1;
  111. while (block.isValid()) {
  112. const BlockInfo &info = m_blocks[block.blockNumber()];
  113. Q_ASSERT(info.hasOffset());
  114. if (info.bottom() > y) {
  115. p_last = block.blockNumber();
  116. break;
  117. }
  118. block = block.next();
  119. }
  120. }
  121. int VTextDocumentLayout::findBlockByPosition(const QPointF &p_point) const
  122. {
  123. int first = 0, last = m_blocks.size() - 1;
  124. int y = p_point.y();
  125. while (first <= last) {
  126. int mid = (first + last) / 2;
  127. const BlockInfo &info = m_blocks[mid];
  128. Q_ASSERT(info.hasOffset());
  129. if (info.top() <= y && info.bottom() > y) {
  130. // Found it.
  131. return mid;
  132. } else if (info.top() > y) {
  133. last = mid - 1;
  134. } else {
  135. first = mid + 1;
  136. }
  137. }
  138. int idx = previousValidBlockNumber(m_blocks.size());
  139. if (y >= m_blocks[idx].bottom()) {
  140. return idx;
  141. }
  142. idx = nextValidBlockNumber(-1);
  143. if (y < m_blocks[idx].top()) {
  144. return idx;
  145. }
  146. Q_ASSERT(false);
  147. return -1;
  148. }
  149. void VTextDocumentLayout::draw(QPainter *p_painter, const PaintContext &p_context)
  150. {
  151. // Find out the blocks.
  152. int first, last;
  153. blockRangeFromRectBS(p_context.clip, first, last);
  154. if (first == -1) {
  155. return;
  156. }
  157. QTextDocument *doc = document();
  158. Q_ASSERT(doc->blockCount() == m_blocks.size());
  159. QPointF offset(m_margin, m_blocks[first].top());
  160. QTextBlock block = doc->findBlockByNumber(first);
  161. QTextBlock lastBlock = doc->findBlockByNumber(last);
  162. QPen oldPen = p_painter->pen();
  163. p_painter->setPen(p_context.palette.color(QPalette::Text));
  164. while (block.isValid()) {
  165. const BlockInfo &info = m_blocks[block.blockNumber()];
  166. Q_ASSERT(info.hasOffset());
  167. const QRectF &rect = info.m_rect;
  168. QTextLayout *layout = block.layout();
  169. if (!block.isVisible()) {
  170. offset.ry() += rect.height();
  171. if (block == lastBlock) {
  172. break;
  173. }
  174. block = block.next();
  175. continue;
  176. }
  177. QTextBlockFormat blockFormat = block.blockFormat();
  178. QBrush bg = blockFormat.background();
  179. if (bg != Qt::NoBrush) {
  180. fillBackground(p_painter, rect, bg);
  181. }
  182. auto selections = formatRangeFromSelection(block, p_context.selections);
  183. layout->draw(p_painter,
  184. offset,
  185. selections,
  186. p_context.clip.isValid() ? p_context.clip : QRectF());
  187. drawBlockImage(p_painter, block, offset);
  188. // Draw the cursor.
  189. int blpos = block.position();
  190. int bllen = block.length();
  191. bool drawCursor = p_context.cursorPosition >= blpos
  192. && p_context.cursorPosition < blpos + bllen;
  193. if (drawCursor
  194. || (p_context.cursorPosition < -1
  195. && !layout->preeditAreaText().isEmpty())) {
  196. int cpos = p_context.cursorPosition;
  197. if (cpos < -1) {
  198. cpos = layout->preeditAreaPosition() - (cpos + 2);
  199. } else {
  200. cpos -= blpos;
  201. }
  202. layout->drawCursor(p_painter, offset, cpos, m_cursorWidth);
  203. }
  204. offset.ry() += rect.height();
  205. if (block == lastBlock) {
  206. break;
  207. }
  208. block = block.next();
  209. }
  210. p_painter->setPen(oldPen);
  211. }
  212. QVector<QTextLayout::FormatRange> VTextDocumentLayout::formatRangeFromSelection(const QTextBlock &p_block,
  213. const QVector<Selection> &p_selections) const
  214. {
  215. QVector<QTextLayout::FormatRange> ret;
  216. int blpos = p_block.position();
  217. int bllen = p_block.length();
  218. for (int i = 0; i < p_selections.size(); ++i) {
  219. const QAbstractTextDocumentLayout::Selection &range = p_selections.at(i);
  220. const int selStart = range.cursor.selectionStart() - blpos;
  221. const int selEnd = range.cursor.selectionEnd() - blpos;
  222. if (selStart < bllen
  223. && selEnd > 0
  224. && selEnd > selStart) {
  225. QTextLayout::FormatRange o;
  226. o.start = selStart;
  227. o.length = selEnd - selStart;
  228. o.format = range.format;
  229. ret.append(o);
  230. } else if (!range.cursor.hasSelection()
  231. && range.format.hasProperty(QTextFormat::FullWidthSelection)
  232. && p_block.contains(range.cursor.position())) {
  233. // For full width selections we don't require an actual selection, just
  234. // a position to specify the line. that's more convenience in usage.
  235. QTextLayout::FormatRange o;
  236. QTextLine l = p_block.layout()->lineForTextPosition(range.cursor.position() - blpos);
  237. o.start = l.textStart();
  238. o.length = l.textLength();
  239. if (o.start + o.length == bllen - 1) {
  240. ++o.length; // include newline
  241. }
  242. o.format = range.format;
  243. ret.append(o);
  244. }
  245. }
  246. return ret;
  247. }
  248. int VTextDocumentLayout::hitTest(const QPointF &p_point, Qt::HitTestAccuracy p_accuracy) const
  249. {
  250. Q_UNUSED(p_accuracy);
  251. int bn = findBlockByPosition(p_point);
  252. if (bn == -1) {
  253. return -1;
  254. }
  255. QTextBlock block = document()->findBlockByNumber(bn);
  256. Q_ASSERT(block.isValid());
  257. QTextLayout *layout = block.layout();
  258. int off = 0;
  259. QPointF pos = p_point - QPointF(m_margin, m_blocks[bn].top());
  260. for (int i = 0; i < layout->lineCount(); ++i) {
  261. QTextLine line = layout->lineAt(i);
  262. const QRectF lr = line.naturalTextRect();
  263. if (lr.top() > pos.y()) {
  264. off = qMin(off, line.textStart());
  265. } else if (lr.bottom() <= pos.y()) {
  266. off = qMax(off, line.textStart() + line.textLength());
  267. } else {
  268. off = line.xToCursor(pos.x(), QTextLine::CursorBetweenCharacters);
  269. break;
  270. }
  271. }
  272. return block.position() + off;
  273. }
  274. int VTextDocumentLayout::pageCount() const
  275. {
  276. return 1;
  277. }
  278. QSizeF VTextDocumentLayout::documentSize() const
  279. {
  280. return QSizeF(m_width, m_height);
  281. }
  282. QRectF VTextDocumentLayout::frameBoundingRect(QTextFrame *p_frame) const
  283. {
  284. Q_UNUSED(p_frame);
  285. return QRectF(0, 0,
  286. qMax(document()->pageSize().width(), m_width), qreal(INT_MAX));
  287. }
  288. QRectF VTextDocumentLayout::blockBoundingRect(const QTextBlock &p_block) const
  289. {
  290. // Sometimes blockBoundingRect() maybe called before documentChanged().
  291. if (!p_block.isValid() || p_block.blockNumber() >= m_blocks.size()) {
  292. return QRectF();
  293. }
  294. const BlockInfo &info = m_blocks[p_block.blockNumber()];
  295. QRectF geo = info.m_rect.adjusted(0, info.m_offset, 0, info.m_offset);
  296. Q_ASSERT(info.hasOffset());
  297. return geo;
  298. }
  299. void VTextDocumentLayout::documentChanged(int p_from, int p_charsRemoved, int p_charsAdded)
  300. {
  301. QTextDocument *doc = document();
  302. int newBlockCount = doc->blockCount();
  303. // Update the margin.
  304. m_margin = doc->documentMargin();
  305. int charsChanged = p_charsRemoved + p_charsAdded;
  306. QTextBlock changeStartBlock = doc->findBlock(p_from);
  307. // May be an invalid block.
  308. QTextBlock changeEndBlock = doc->findBlock(qMax(0, p_from + charsChanged));
  309. bool needRelayout = false;
  310. if (changeStartBlock == changeEndBlock
  311. && newBlockCount == m_blockCount) {
  312. // Change single block internal only.
  313. QTextBlock block = changeStartBlock;
  314. if (block.isValid() && block.length()) {
  315. QRectF oldBr = blockBoundingRect(block);
  316. clearBlockLayout(block);
  317. layoutBlock(block);
  318. QRectF newBr = blockBoundingRect(block);
  319. // Only one block is affected.
  320. if (newBr.height() == oldBr.height()) {
  321. // Update document size.
  322. updateDocumentSizeWithOneBlockChanged(block.blockNumber());
  323. emit updateBlock(block);
  324. return;
  325. }
  326. }
  327. } else {
  328. // Clear layout of all affected blocks.
  329. QTextBlock block = changeStartBlock;
  330. do {
  331. clearBlockLayout(block);
  332. if (block == changeEndBlock) {
  333. break;
  334. }
  335. block = block.next();
  336. } while(block.isValid());
  337. needRelayout = true;
  338. }
  339. updateBlockCount(newBlockCount, changeStartBlock.blockNumber());
  340. if (needRelayout) {
  341. // Relayout all affected blocks.
  342. QTextBlock block = changeStartBlock;
  343. do {
  344. layoutBlock(block);
  345. if (block == changeEndBlock) {
  346. break;
  347. }
  348. block = block.next();
  349. } while(block.isValid());
  350. }
  351. updateDocumentSize();
  352. // TODO: Update the view of all the blocks after changeStartBlock.
  353. const BlockInfo &firstInfo = m_blocks[changeStartBlock.blockNumber()];
  354. emit update(QRectF(0., firstInfo.m_offset, 1000000000., 1000000000.));
  355. }
  356. void VTextDocumentLayout::clearBlockLayout(QTextBlock &p_block)
  357. {
  358. p_block.clearLayout();
  359. int num = p_block.blockNumber();
  360. if (num < m_blocks.size()) {
  361. m_blocks[num].reset();
  362. clearOffsetFrom(num + 1);
  363. }
  364. }
  365. void VTextDocumentLayout::clearOffsetFrom(int p_blockNumber)
  366. {
  367. for (int i = p_blockNumber; i < m_blocks.size(); ++i) {
  368. if (!m_blocks[i].hasOffset()) {
  369. Q_ASSERT(validateBlocks());
  370. break;
  371. }
  372. m_blocks[i].m_offset = -1;
  373. }
  374. }
  375. void VTextDocumentLayout::fillOffsetFrom(int p_blockNumber)
  376. {
  377. qreal offset = m_blocks[p_blockNumber].bottom();
  378. for (int i = p_blockNumber + 1; i < m_blocks.size(); ++i) {
  379. BlockInfo &info = m_blocks[i];
  380. if (!info.m_rect.isNull()) {
  381. info.m_offset = offset;
  382. offset += info.m_rect.height();
  383. } else {
  384. break;
  385. }
  386. }
  387. }
  388. bool VTextDocumentLayout::validateBlocks() const
  389. {
  390. bool valid = true;
  391. for (int i = 0; i < m_blocks.size(); ++i) {
  392. const BlockInfo &info = m_blocks[i];
  393. if (!info.hasOffset()) {
  394. valid = false;
  395. } else if (!valid) {
  396. return false;
  397. }
  398. }
  399. return true;
  400. }
  401. void VTextDocumentLayout::updateBlockCount(int p_count, int p_changeStartBlock)
  402. {
  403. if (m_blockCount != p_count) {
  404. m_blockCount = p_count;
  405. m_blocks.resize(m_blockCount);
  406. // Fix m_blocks.
  407. QTextBlock block = document()->findBlockByNumber(p_changeStartBlock);
  408. while (block.isValid()) {
  409. BlockInfo &info = m_blocks[block.blockNumber()];
  410. info.reset();
  411. QRectF br = blockRectFromTextLayout(block);
  412. if (!br.isNull()) {
  413. info.m_rect = br;
  414. }
  415. block = block.next();
  416. }
  417. }
  418. }
  419. void VTextDocumentLayout::layoutBlock(const QTextBlock &p_block)
  420. {
  421. QTextDocument *doc = document();
  422. Q_ASSERT(m_margin == doc->documentMargin());
  423. // The height (y) of the next line.
  424. qreal height = 0;
  425. QTextLayout *tl = p_block.layout();
  426. QTextOption option = doc->defaultTextOption();
  427. tl->setTextOption(option);
  428. int extraMargin = 0;
  429. if (option.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
  430. QFontMetrics fm(p_block.charFormat().font());
  431. extraMargin += fm.width(QChar(0x21B5));
  432. }
  433. qreal availableWidth = doc->pageSize().width();
  434. if (availableWidth <= 0) {
  435. availableWidth = qreal(INT_MAX);
  436. }
  437. availableWidth -= (2 * m_margin + extraMargin + m_cursorMargin);
  438. tl->beginLayout();
  439. while (true) {
  440. QTextLine line = tl->createLine();
  441. if (!line.isValid()) {
  442. break;
  443. }
  444. line.setLeadingIncluded(true);
  445. line.setLineWidth(availableWidth);
  446. height += m_lineLeading;
  447. line.setPosition(QPointF(m_margin, height));
  448. height += line.height();
  449. }
  450. tl->endLayout();
  451. // Set this block's line count to its layout's line count.
  452. // That is one block may occupy multiple visual lines.
  453. const_cast<QTextBlock&>(p_block).setLineCount(p_block.isVisible() ? tl->lineCount() : 0);
  454. // Update the info about this block.
  455. finishBlockLayout(p_block);
  456. }
  457. void VTextDocumentLayout::finishBlockLayout(const QTextBlock &p_block)
  458. {
  459. // Update rect and offset.
  460. Q_ASSERT(p_block.isValid());
  461. int num = p_block.blockNumber();
  462. Q_ASSERT(m_blocks.size() > num);
  463. BlockInfo &info = m_blocks[num];
  464. info.reset();
  465. info.m_rect = blockRectFromTextLayout(p_block);
  466. Q_ASSERT(!info.m_rect.isNull());
  467. int pre = previousValidBlockNumber(num);
  468. if (pre == -1) {
  469. info.m_offset = 0;
  470. } else if (m_blocks[pre].hasOffset()) {
  471. info.m_offset = m_blocks[pre].bottom();
  472. }
  473. if (info.hasOffset()) {
  474. fillOffsetFrom(num);
  475. }
  476. }
  477. int VTextDocumentLayout::previousValidBlockNumber(int p_number) const
  478. {
  479. return p_number >= 0 ? p_number - 1 : -1;
  480. }
  481. int VTextDocumentLayout::nextValidBlockNumber(int p_number) const
  482. {
  483. if (p_number <= -1) {
  484. return 0;
  485. } else if (p_number >= m_blocks.size() - 1) {
  486. return -1;
  487. } else {
  488. return p_number + 1;
  489. }
  490. }
  491. void VTextDocumentLayout::updateDocumentSize()
  492. {
  493. // The last valid block.
  494. int idx = previousValidBlockNumber(m_blocks.size());
  495. Q_ASSERT(idx > -1);
  496. if (m_blocks[idx].hasOffset()) {
  497. int oldHeight = m_height;
  498. int oldWidth = m_width;
  499. m_height = m_blocks[idx].bottom();
  500. m_width = 0;
  501. for (int i = 0; i < m_blocks.size(); ++i) {
  502. const BlockInfo &info = m_blocks[i];
  503. Q_ASSERT(info.hasOffset());
  504. if (m_width < info.m_rect.width()) {
  505. m_width = info.m_rect.width();
  506. m_maximumWidthBlockNumber = i;
  507. }
  508. }
  509. if (oldHeight != m_height
  510. || oldWidth != m_width) {
  511. emit documentSizeChanged(documentSize());
  512. }
  513. }
  514. }
  515. void VTextDocumentLayout::setCursorWidth(int p_width)
  516. {
  517. m_cursorWidth = p_width;
  518. }
  519. int VTextDocumentLayout::cursorWidth() const
  520. {
  521. return m_cursorWidth;
  522. }
  523. QRectF VTextDocumentLayout::blockRectFromTextLayout(const QTextBlock &p_block)
  524. {
  525. QTextLayout *tl = p_block.layout();
  526. if (tl->lineCount() < 1) {
  527. return QRectF();
  528. }
  529. QRectF tlRect = tl->boundingRect();
  530. QRectF br(QPointF(0, 0), tlRect.bottomRight());
  531. // Do not know why. Copied from QPlainTextDocumentLayout.
  532. if (tl->lineCount() == 1) {
  533. br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
  534. }
  535. // Handle block image.
  536. if (m_blockImageEnabled) {
  537. const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
  538. if (info && !info->m_imageSize.isNull()) {
  539. int maximumWidth = tlRect.width();
  540. int padding;
  541. QSize size;
  542. adjustImagePaddingAndSize(info, maximumWidth, padding, size);
  543. int dw = padding + size.width() + m_margin - br.width();
  544. int dh = size.height() + m_lineLeading;
  545. br.adjust(0, 0, dw > 0 ? dw : 0, dh);
  546. }
  547. }
  548. br.adjust(0, 0, m_margin + m_cursorMargin, 0);
  549. // Add bottom margin.
  550. if (!p_block.next().isValid()) {
  551. br.adjust(0, 0, 0, m_margin);
  552. }
  553. return br;
  554. }
  555. void VTextDocumentLayout::updateDocumentSizeWithOneBlockChanged(int p_blockNumber)
  556. {
  557. const BlockInfo &info = m_blocks[p_blockNumber];
  558. qreal width = info.m_rect.width();
  559. if (width > m_width) {
  560. m_width = width;
  561. m_maximumWidthBlockNumber = p_blockNumber;
  562. emit documentSizeChanged(documentSize());
  563. } else if (width < m_width && p_blockNumber == m_maximumWidthBlockNumber) {
  564. // Shrink the longest block.
  565. updateDocumentSize();
  566. }
  567. }
  568. void VTextDocumentLayout::setLineLeading(qreal p_leading)
  569. {
  570. if (p_leading >= 0) {
  571. m_lineLeading = p_leading;
  572. }
  573. }
  574. void VTextDocumentLayout::setImageWidthConstrainted(bool p_enabled)
  575. {
  576. if (m_imageWidthConstrainted == p_enabled) {
  577. return;
  578. }
  579. m_imageWidthConstrainted = p_enabled;
  580. relayout();
  581. }
  582. void VTextDocumentLayout::setBlockImageEnabled(bool p_enabled)
  583. {
  584. if (m_blockImageEnabled == p_enabled) {
  585. return;
  586. }
  587. m_blockImageEnabled = p_enabled;
  588. relayout();
  589. }
  590. void VTextDocumentLayout::adjustImagePaddingAndSize(const VBlockImageInfo2 *p_info,
  591. int p_maximumWidth,
  592. int &p_padding,
  593. QSize &p_size) const
  594. {
  595. const int minimumImageWidth = 400;
  596. p_padding = p_info->m_padding;
  597. p_size = p_info->m_imageSize;
  598. if (!m_imageWidthConstrainted) {
  599. return;
  600. }
  601. int availableWidth = p_maximumWidth - p_info->m_padding;
  602. if (availableWidth < p_info->m_imageSize.width()) {
  603. // Need to resize the width.
  604. if (availableWidth >= minimumImageWidth) {
  605. p_size.scale(availableWidth, p_size.height(), Qt::KeepAspectRatio);
  606. } else {
  607. // Omit the padding.
  608. p_padding = 0;
  609. p_size.scale(p_maximumWidth, p_size.height(), Qt::KeepAspectRatio);
  610. }
  611. }
  612. }
  613. void VTextDocumentLayout::drawBlockImage(QPainter *p_painter,
  614. const QTextBlock &p_block,
  615. const QPointF &p_offset)
  616. {
  617. if (!m_blockImageEnabled) {
  618. return;
  619. }
  620. const VBlockImageInfo2 *info = m_imageMgr->findImageInfoByBlock(p_block.blockNumber());
  621. if (!info || info->m_imageSize.isNull()) {
  622. return;
  623. }
  624. const QPixmap *image = m_imageMgr->findImage(info->m_imageName);
  625. Q_ASSERT(image);
  626. // Draw block image.
  627. QTextLayout *tl = p_block.layout();
  628. QRectF tlRect = tl->boundingRect();
  629. int maximumWidth = tlRect.width();
  630. int padding;
  631. QSize size;
  632. adjustImagePaddingAndSize(info, maximumWidth, padding, size);
  633. QRect targetRect(p_offset.x() + padding,
  634. p_offset.y() + tlRect.height() + m_lineLeading,
  635. size.width(),
  636. size.height());
  637. p_painter->drawPixmap(targetRect, *image);
  638. }
  639. void VTextDocumentLayout::relayout()
  640. {
  641. QTextDocument *doc = document();
  642. // Update the margin.
  643. m_margin = doc->documentMargin();
  644. QTextBlock block = doc->lastBlock();
  645. while (block.isValid()) {
  646. clearBlockLayout(block);
  647. block = block.previous();
  648. }
  649. block = doc->firstBlock();
  650. while (block.isValid()) {
  651. layoutBlock(block);
  652. block = block.next();
  653. }
  654. updateDocumentSize();
  655. emit update(QRectF(0., 0., 1000000000., 1000000000.));
  656. }
  657. void VTextDocumentLayout::relayout(const QSet<int> &p_blocks)
  658. {
  659. if (p_blocks.isEmpty()) {
  660. return;
  661. }
  662. QTextDocument *doc = document();
  663. for (auto bn : p_blocks) {
  664. QTextBlock block = doc->findBlockByNumber(bn);
  665. if (block.isValid()) {
  666. clearBlockLayout(block);
  667. layoutBlock(block);
  668. emit updateBlock(block);
  669. }
  670. }
  671. updateDocumentSize();
  672. }