X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fchatitem.cpp;h=ff81259cfa385645df3f09b3efa2fd7f2f7c87ce;hp=7c0cc9a6a375e7345260c524f012a4126fd4a573;hb=209c692cb560ba979442d2f75ed08b4f6adf3120;hpb=0b9f74984780aacbe85ca04c44ec6304c86557c2 diff --git a/src/qtui/chatitem.cpp b/src/qtui/chatitem.cpp index 7c0cc9a6..ff81259c 100644 --- a/src/qtui/chatitem.cpp +++ b/src/qtui/chatitem.cpp @@ -20,18 +20,17 @@ #include #include -#include -#include - -#include +#include +#include #include "chatitem.h" +#include "chatlinemodel.h" +#include "qtui.h" ChatItem::ChatItem(const QPersistentModelIndex &index_, QGraphicsItem *parent) : QGraphicsItem(parent), _index(index_) { - //if(_wrapMode == WordWrap) { - // setFlags(QGraphicsItem::ItemClipsToShape, true); - //} - + _fontMetrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value().at(0).second); + _layout = 0; + _lines = 0; } ChatItem::~ChatItem() { @@ -40,49 +39,138 @@ ChatItem::~ChatItem() { QVariant ChatItem::data(int role) const { if(!_index.isValid()) { - qWarning() << "ChatItem::data(): Model index is invalid!"; + qWarning() << "ChatItem::data(): Model index is invalid!" << _index; return QVariant(); } return _index.data(role); } -QRectF ChatItem::boundingRect() const { - return QRectF(0, 0, 500,20); +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; } -void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - Q_UNUSED(option); Q_UNUSED(widget); - - painter->drawRect(boundingRect()); - painter->drawText(boundingRect(), data(MessageModel::DisplayRole).toString()); +int ChatItem::heightForWidth(int width) { + if(data(ChatLineModel::ColumnTypeRole).toUInt() != ChatLineModel::ContentsColumn) + 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 - 4*fontMetrics()->averageCharWidth(); + } + } + return _lines * fontMetrics()->lineSpacing(); } - -/* -void ChatItem::setWidth(int w) { - _width = w; - layout(); +void ChatItem::layout() { + 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(); + QTextLayout::FormatRange range; + int i = 0; + for(i = 0; i < formatList.count(); i++) { + range.format = QtUi::style()->mergedFormat(formatList.at(i).second); + range.start = formatList.at(i).first; + if(i > 0) formatRanges.last().length = range.start - formatRanges.last().start; + formatRanges.append(range); + } + if(i > 0) formatRanges.last().length = _layout->text().length() - formatRanges.last().start; + _layout->setAdditionalFormats(formatRanges); + updateLayout(); } -void ChatItem::setTextOption(const QTextOption &option) { - _textOption = option; - layout(); -} +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 { + QTextLine line = _layout->createLine(); + if (!line.isValid()) + break; + + if(word.width >= width()) { + line.setLineWidth(width()); + word.width -= line.naturalTextWidth(); + word.start = line.textStart() + line.textLength(); + //qDebug() << "setting width: " << width(); + } 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--; qDebug() << "trigger!" << _lines << _layout->text(); + break; + } + } + } + int lastcol = wordidx < wrapList.count() ? word.start : _layout->text().length(); + line.setNumColumns(lastcol - line.textStart());// qDebug() << "setting cols:" << lastcol - line.textStart(); + } -QTextOption ChatItem::textOption() const { - return _textOption; + h += fontMetrics()->leading(); + line.setPosition(QPointF(0, h)); + h += line.height(); + } + _layout->endLayout(); } -QString ChatItem::text() const { - return _layout.text(); +void ChatItem::clearLayout() { + delete _layout; + _layout = 0; } -void ChatItem::setText(const UiStyle::StyledText &text) { - _layout.setText(text.text); - _layout.setAdditionalFormats(text.formatList); +void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + 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); + } } +/* void ChatItem::layout() { if(!_layout.additionalFormats().count()) return; // no text set if(_width <= 0) return; @@ -138,6 +226,3 @@ void ChatItem::mouseMoveEvent ( QGraphicsSceneMouseEvent * event ) { } else QGraphicsTextItem::mouseMoveEvent(event); } */ - - -