#include "qtui.h"
ChatItem::ChatItem(const QPersistentModelIndex &index_, QGraphicsItem *parent) : QGraphicsItem(parent), _index(index_) {
- QFontMetricsF *metrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value<UiStyle::FormatList>().at(0).second);
- _lineHeight = metrics->lineSpacing();
- _lineLeading = metrics->leading();
+ _fontMetrics = QtUi::style()->fontMetrics(data(ChatLineModel::FormatRole).value<UiStyle::FormatList>().at(0).second);
_layout = 0;
+ _lines = 0;
}
ChatItem::~ChatItem() {
}
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<ChatLineModel::WrapList>();
+ _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<QTextLayout::FormatRange> formatRanges;
UiStyle::FormatList formatList = data(MessageModel::FormatRole).value<UiStyle::FormatList>();
}
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<ChatLineModel::WrapList>();
+ if(!wrapList.count()) return; // empty chatitem
+ int wordidx = 0;
+ ChatLineModel::Word word = wrapList.at(0);
+
qreal h = 0;
_layout->beginLayout();
forever {
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();
}
Q_UNUSED(option); Q_UNUSED(widget);
layout();
_layout->draw(painter, QPointF(0,0), QVector<QTextLayout::FormatRange>(), 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);
+ }
}
/*