X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fqtui%2Fchatitem.cpp;h=c9724e270ad12868a32435d1b1d108c0905016f1;hb=67bc54a2837fcb19995beb461984bd1a3d616ee7;hp=c12d5c5b92b5fe1712b1c52c42415fbad3d5be8f;hpb=84ff541e038763977a99642a90af11ee995c440d;p=quassel.git diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index c12d5c5b..c9724e27 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -28,10 +28,9 @@ #include "qtui.h" ChatItem::ChatItem(const QPersistentModelIndex &index_, QGraphicsItem *parent) : QGraphicsItem(parent), _index(index_) { - QFontMetricsF *metrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value().at(0).second); - _lineHeight = metrics->lineSpacing(); - _lineLeading = metrics->leading(); + _fontMetrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value().at(0).second); _layout = 0; + _lines = 0; } ChatItem::~ChatItem() { @@ -47,38 +46,51 @@ QVariant ChatItem::data(int role) const { } int ChatItem::setWidth(int w) { + w -= 10; if(w == _boundingRect.width()) return _boundingRect.height(); int h = heightForWidth(w); _boundingRect.setWidth(w); _boundingRect.setHeight(h); + if(haveLayout()) updateLayout(); return h; } int ChatItem::heightForWidth(int width) { if(data(ChatLineModel::ColumnTypeRole).toUInt() != ChatLineModel::ContentsColumn) - return _lineHeight; // only contents can be multi-line - - QVariantList wrapList = data(ChatLineModel::WrapListRole).toList(); - int lines = 1; - int offset = 0; - for(int i = 0; i < wrapList.count(); i+=2) { - if(wrapList.at(i+1).toUInt() - offset < width) continue; - lines++; - if(i > 0) { - if(offset < wrapList.at(i-1).toUInt()) offset = wrapList.at(i-1).toUInt(); - else offset += width; - } else { - offset += width; + return fontMetrics()->lineSpacing(); // only contents can be multi-line + + ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value(); + _lines = 1; + qreal w = 0; + for(int i = 0; i < wrapList.count(); i++) { + w += wrapList.at(i).width; + if(w <= width) { + w += wrapList.at(i).trailing; + continue; + } + _lines++; + w = wrapList.at(i).width; + while(w >= width) { // handle words longer than a line + _lines++; + // We do not want to compute an exact split position (that would require us to calculate glyph widths + // and also apply formats in this step...) + // Just using width should be a good estimate, but since a character can't be split in the middle, we + // subtract averageCharWidth as well... sometimes this won't be enough, but meh. + w -= width - fontMetrics()->averageCharWidth(); } - i-=2; + w += wrapList.at(i).trailing; } - return lines * _lineHeight; + return _lines * fontMetrics()->lineSpacing(); } void ChatItem::layout() { - if(_layout) return; + if(haveLayout()) return; _layout = new QTextLayout(data(MessageModel::DisplayRole).toString()); + QTextOption option; + option.setWrapMode(QTextOption::WrapAnywhere); + _layout->setTextOption(option); + // Convert format information into a FormatRange QList formatRanges; UiStyle::FormatList formatList = data(MessageModel::FormatRole).value(); @@ -92,8 +104,18 @@ void ChatItem::layout() { } if(i > 0) formatRanges.last().length = _layout->text().length() - formatRanges.last().start; _layout->setAdditionalFormats(formatRanges); + updateLayout(); +} + +void ChatItem::updateLayout() { + if(!haveLayout()) layout(); // Now layout + ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value(); + if(!wrapList.count()) return; // empty chatitem + int wordidx = 0; + ChatLineModel::Word word = wrapList.at(0); + qreal h = 0; _layout->beginLayout(); forever { @@ -101,8 +123,29 @@ void ChatItem::layout() { if (!line.isValid()) break; - line.setLineWidth(width()); - h += _lineLeading; + if(word.width >= width()) { + line.setLineWidth(width()); + word.width -= line.naturalTextWidth(); + word.start = line.textStart() + line.textLength(); + } else { + int w = 0; + while((w += word.width) <= width() && wordidx < wrapList.count()) { + w += word.trailing; + if(++wordidx < wrapList.count()) word = wrapList.at(wordidx); + else { + // last word (and it fits), but if we expected an extra line, wrap anyway here + // yeah, this is cheating, but much cheaper than computing widths ourself + if(_layout->lineCount() < _lines) { + wordidx--; + break; + } + } + } + int lastcol = wordidx < wrapList.count() ? word.start : _layout->text().length(); + line.setNumColumns(lastcol - line.textStart());// qDebug() << "setting cols:" << lastcol - line.textStart(); + } + + h += fontMetrics()->leading(); line.setPosition(QPointF(0, h)); h += line.height(); } @@ -118,6 +161,13 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q_UNUSED(option); Q_UNUSED(widget); layout(); _layout->draw(painter, QPointF(0,0), QVector(), boundingRect()); + painter->drawRect(boundingRect()); + int width = 0; + QVariantList wrapList = data(ChatLineModel::WrapListRole).toList(); + for(int i = 2; i < wrapList.count(); i+=2) { + QRect r(wrapList[i-1].toUInt(), 0, wrapList[i+1].toUInt() - wrapList[i-1].toUInt(), fontMetrics()->lineSpacing()); + painter->drawRect(r); + } } /*