X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fqtui%2Fchatitem.cpp;h=1f7e9ac628959b24d6da2170a89ed7ac64e7507f;hb=d7e94af265bd78598bf52ce5fde6c7a1eba9e149;hp=819877aeb6d666fdbb0ac5d6134ff43e100ff21c;hpb=064dcac965d1e724a0434683685a24ec7e6ba855;p=quassel.git diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 819877ae..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,20 +387,42 @@ ContentsChatItemPrivate *ContentsChatItem::privateData() const { } qreal ContentsChatItem::setGeometryByWidth(qreal w) { - if(w == width()) { - //qDebug() << Q_FUNC_INFO << "Geometry change requested with identical width!"; - } - // We use this for reloading layout info as well - //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; - //} - return height(); + 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(); + } + layout->endLayout(); } // NOTE: This method is not threadsafe and not reentrant! @@ -669,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; +} + +/*************************************************************************************************/ +