X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fchatitem.cpp;h=1f7e9ac628959b24d6da2170a89ed7ac64e7507f;hp=2159f3f3ffb641ff91be39dada0a9ae05ae99a89;hb=62fc1acc7f17cb2397ba2d7e879a1266c0a7672c;hpb=5199330f0b249b20c27cd372d995909f97433786 diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 2159f3f3..1f7e9ac6 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -89,15 +89,11 @@ void ChatItem::initLayoutHelper(QTextLayout *layout, QTextOption::WrapMode wrapM } void ChatItem::doLayout(QTextLayout *layout) const { - qreal h = 0; layout->beginLayout(); - forever { - QTextLine line = layout->createLine(); - if(!line.isValid()) - break; + QTextLine line = layout->createLine(); + if(line.isValid()) { line.setLineWidth(width()); - line.setPosition(QPointF(0, h)); - h += line.height(); + line.setPosition(QPointF(0,0)); } layout->endLayout(); } @@ -375,7 +371,7 @@ ContentsChatItem::ContentsChatItem(const qreal &width, const QPointF &pos, QGrap } QFontMetricsF *ContentsChatItem::fontMetrics() const { - return QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value().at(0).second); + return QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value().at(0).second, 0); } ContentsChatItem::~ContentsChatItem() { @@ -391,16 +387,42 @@ ContentsChatItemPrivate *ContentsChatItem::privateData() const { } qreal ContentsChatItem::setGeometryByWidth(qreal w) { - if(w != width()) { + // We use this for reloading layout info as well, so we can't bail out if the width doesn't change + + // compute height + int lines = 1; + WrapColumnFinder finder(this); + while(finder.nextWrapColumn(w) > 0) + lines++; + qreal h = lines * fontMetrics()->lineSpacing(); + delete _data; + _data = 0; + + if(w != width() || h != height()) { prepareGeometryChange(); - setWidth(w); - QTextLayout layout; - initLayout(&layout); - setHeight(layout.boundingRect().height()); - delete _data; - _data = 0; + setGeometry(w, h); + } + return h; +} + +void ContentsChatItem::doLayout(QTextLayout *layout) const { + ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value(); + if(!wrapList.count()) return; // empty chatitem + + qreal h = 0; + WrapColumnFinder finder(this); + layout->beginLayout(); + forever { + QTextLine line = layout->createLine(); + if(!line.isValid()) + break; + + int col = finder.nextWrapColumn(width()); + line.setNumColumns(col >= 0 ? col - line.textStart() : layout->text().length()); + line.setPosition(QPointF(0, h)); + h += fontMetrics()->lineSpacing(); } - return height(); + layout->endLayout(); } // NOTE: This method is not threadsafe and not reentrant! @@ -665,3 +687,66 @@ void ContentsChatItem::clearWebPreview() { /*************************************************************************************************/ +ContentsChatItem::WrapColumnFinder::WrapColumnFinder(const ChatItem *_item) + : item(_item), + wrapList(item->data(ChatLineModel::WrapListRole).value()), + wordidx(0), + lineCount(0), + choppedTrailing(0) +{ +} + +ContentsChatItem::WrapColumnFinder::~WrapColumnFinder() { +} + +qint16 ContentsChatItem::WrapColumnFinder::nextWrapColumn(qreal width) { + if(wordidx >= wrapList.count()) + return -1; + + lineCount++; + qreal targetWidth = lineCount * width + choppedTrailing; + + qint16 start = wordidx; + qint16 end = wrapList.count() - 1; + + // check if the whole line fits + if(wrapList.at(end).endX <= targetWidth) // || start == end) + return -1; + + // check if we have a very long word that needs inter word wrap + if(wrapList.at(start).endX > targetWidth) { + if(!line.isValid()) { + item->initLayoutHelper(&layout, QTextOption::NoWrap); + layout.beginLayout(); + line = layout.createLine(); + layout.endLayout(); + } + return line.xToCursor(targetWidth, QTextLine::CursorOnCharacter); + } + + while(true) { + if(start + 1 == end) { + wordidx = end; + const ChatLineModel::Word &lastWord = wrapList.at(start); // the last word we were able to squeeze in + + // both cases should be cought preliminary + Q_ASSERT(lastWord.endX <= targetWidth); // ensure that "start" really fits in + Q_ASSERT(end < wrapList.count()); // ensure that start isn't the last word + + choppedTrailing += lastWord.trailing - (targetWidth - lastWord.endX); + return wrapList.at(wordidx).start; + } + + qint16 pivot = (end + start) / 2; + if(wrapList.at(pivot).endX > targetWidth) { + end = pivot; + } else { + start = pivot; + } + } + Q_ASSERT(false); + return -1; +} + +/*************************************************************************************************/ +