Reworking the wordwrap stuff.
authorManuel Nickschas <sputnick@quassel-irc.org>
Sat, 14 Jun 2008 23:37:08 +0000 (01:37 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Sat, 2 Aug 2008 13:17:09 +0000 (15:17 +0200)
Still has some edges and cornercases to iron out though...

src/qtui/chatitem.cpp
src/qtui/chatitem.h
src/qtui/chatlinemodel.cpp
src/qtui/chatlinemodel.h
src/qtui/chatlinemodelitem.cpp
src/qtui/chatlinemodelitem.h

index 6efe8c2..17189f9 100644 (file)
@@ -60,19 +60,21 @@ 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();
+  ChatLineModel::WrapList wrapList = data(ChatLineModel::WrapListRole).value<ChatLineModel::WrapList>();
   int lines = 1;
-  int offset = 0;
-  for(int i = 0; i < wrapList.count(); i+=2) {
-    if(wrapList.at(i+1).toUInt() - offset < width) continue;
+  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++;
-    if(i > 0) {
-      if(offset < wrapList.at(i-1).toUInt()) offset = wrapList.at(i-1).toUInt();
-      else offset += width;
-    } else {
-      offset += width;
+    w = wrapList.at(i).width;
+    while(w >= width) {
+      lines++;
+      w -= width;
     }
-    i-=2;
   }
   return lines * _lineHeight;
 }
@@ -126,6 +128,12 @@ void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
   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(), _lineHeight);
+    painter->drawRect(r);
+  }
 }
 
 /*
index d32b3cc..956af0e 100644 (file)
@@ -74,6 +74,7 @@ class ChatItem : public QGraphicsItem {
     QPersistentModelIndex _index;
 
     QTextLayout *_layout;
+    QList<quint16> _wrapPositions;
 };
 
 #endif
index 3cf68b1..28d77fb 100644 (file)
@@ -23,7 +23,8 @@
 #include "chatlinemodelitem.h"
 
 ChatLineModel::ChatLineModel(QObject *parent) : MessageModel(parent) {
-
+  qRegisterMetaType<WrapList>("ChatLineModel::WrapList");
+  qRegisterMetaTypeStreamOperators<WrapList>("ChatLineModel::WrapList");
 
 }
 
@@ -36,3 +37,24 @@ MessageModelItem *ChatLineModel::createMessageModelItem(const Message &msg) {
   return new ChatLineModelItem(msg);
 
 }
+
+
+QDataStream &operator<<(QDataStream &out, const ChatLineModel::WrapList wplist) {
+  out << wplist.count();
+  ChatLineModel::WrapList::const_iterator it = wplist.begin();
+  while(it != wplist.end()) {
+    out << (*it).start << (*it).width << (*it).trailing;
+    ++it;
+  }
+  return out;
+}
+
+QDataStream &operator>>(QDataStream &in, ChatLineModel::WrapList &wplist) {
+  quint16 cnt;
+  in >> cnt;
+  wplist.resize(cnt);
+  for(quint16 i = 0; i < cnt; i++) {
+    in >> wplist[i].start >> wplist[i].width >> wplist[i].trailing;
+  }
+  return in;
+}
index 779b5f2..a1e2c69 100644 (file)
@@ -34,10 +34,24 @@ class ChatLineModel : public MessageModel {
     ChatLineModel(QObject *parent = 0);
     virtual ~ChatLineModel();
 
+    /// Used to store information about words to be used for wrapping
+    struct Word {
+      quint16 start;
+      qreal width;
+      qreal trailing;
+    };
+
+    typedef QVector<Word> WrapList;
+  
   protected:
     virtual MessageModelItem *createMessageModelItem(const Message &);
 
 };
 
+QDataStream &operator<<(QDataStream &out, const ChatLineModel::WrapList);
+QDataStream &operator>>(QDataStream &in, ChatLineModel::WrapList &);
+
+Q_DECLARE_METATYPE(ChatLineModel::WrapList);
+
 #endif
 
index 9529412..38663bb 100644 (file)
@@ -58,10 +58,7 @@ QVariant ChatLineModelItem::data(int column, int role) const {
       return QVariant::fromValue<UiStyle::FormatList>(part->formatList);
     case ChatLineModel::WrapListRole:
       if(column != ChatLineModel::ContentsColumn) return QVariant();
-      QVariantList wrapList;
-      typedef QPair<quint16, quint16> WrapPoint;  // foreach can't parse templated params
-      foreach(WrapPoint pair, _wrapList) wrapList << pair.first << pair.second;
-      return wrapList;
+      return QVariant::fromValue<ChatLineModel::WrapList>(_wrapList);
   }
 
   return MessageModelItem::data(column, role);
@@ -71,33 +68,61 @@ bool ChatLineModelItem::setData(int column, const QVariant &value, int role) {
   return false;
 }
 
+// compute the width of a text snippet
+qreal ChatLineModelItem::snippetWidth(int start, int end, QFontMetricsF *&metrics, int &formatListIdx, int &formatEnd) {
+  qreal width = 0;
+  while(start < end) {
+    if(formatEnd <= start) {
+      formatListIdx++;
+      formatEnd = _contents.formatList.count() > formatListIdx+1 ? _contents.formatList[formatListIdx+1].first
+                                                                 : _contents.plainText.length();
+      metrics = QtUi::style()->fontMetrics(_contents.formatList[formatListIdx].second);
+      Q_ASSERT(formatEnd > start);
+    }
+    int i = qMin(end, formatEnd);
+    width += metrics->width(_contents.plainText.mid(start, i - start));
+    start = i;
+  }
+  return width;
+}
+
 void ChatLineModelItem::computeWrapList() {
-  WrapList wplist;  // use a temp list which we'll later copy into a QVector for efficiency
+  enum Mode { SearchStart, SearchEnd };
+
+  QList<ChatLineModel::Word> wplist;  // use a temp list which we'll later copy into a QVector for efficiency
   QTextBoundaryFinder finder(QTextBoundaryFinder::Word, _contents.plainText);
-  int idx;
+  int idx, oldidx;
+  qreal pxpos = 0;
   int flistidx = -1;
   int fmtend = -1;
-  QFontMetricsF *metrics;
-  QPair<quint16, quint16> wp(0, 0);
+  bool wordStart = false; bool wordEnd = false;
+  QFontMetricsF *metrics = 0;
+  Mode mode = SearchEnd;
+  ChatLineModel::Word word;
+  word.start = 0;
   do {
     idx = finder.toNextBoundary();
     if(idx < 0) idx = _contents.plainText.length();
-    else if(finder.boundaryReasons() != QTextBoundaryFinder::StartWord) continue;
-    int start = wp.first;
-    while(start < idx) {
-      if(fmtend <= start) {
-        flistidx++;
-        fmtend = _contents.formatList.count() > flistidx+1 ? _contents.formatList[flistidx+1].first
-                                                           : _contents.plainText.length();
-        metrics = QtUi::style()->fontMetrics(_contents.formatList[flistidx].second);
-        Q_ASSERT(fmtend > start);
-      }
-      int i = qMin(idx, fmtend);
-      wp.second += metrics->width(_contents.plainText.mid(start, i - start));
-      start = i;
+    wordStart = finder.boundaryReasons().testFlag(QTextBoundaryFinder::StartWord);
+    wordEnd = finder.boundaryReasons().testFlag(QTextBoundaryFinder::EndWord);
+
+    //qDebug() << wordStart << wordEnd << _contents.plainText.left(idx) << _contents.plainText.mid(idx);
+
+    if(mode == SearchEnd || !wordStart && wordEnd) {
+      if(wordStart || !wordEnd) continue;
+      oldidx = idx;
+      mode = SearchStart;
+      continue;
+    }
+    // mode == SearchStart
+    word.width = snippetWidth(word.start, oldidx, metrics, flistidx, fmtend);
+    word.trailing = snippetWidth(oldidx, idx, metrics, flistidx, fmtend);
+    wplist.append(word);
+
+    if(wordStart) {
+      word.start = idx;
+      mode = SearchEnd;
     }
-    wplist.append(wp);
-    wp.first = idx;
   } while(finder.isAtBoundary());
 
   // A QVector needs less space than a QList
index f01b4e1..b76ea68 100644 (file)
 #include <QVector>
 #include <QPair>
 
-#include "messagemodel.h"
+#include "chatlinemodel.h"
 #include "uistyle.h"
 
 class ChatLineModelItem : public MessageModelItem {
 
   public:
+
     ChatLineModelItem(const Message &);
     //virtual ~ChatLineModelItem() {};
 
@@ -37,9 +38,8 @@ class ChatLineModelItem : public MessageModelItem {
     virtual bool setData(int column, const QVariant &value, int role);
 
   private:
-    typedef QVector<QPair<quint16, quint16> > WrapList;
-
     void computeWrapList();
+    qreal snippetWidth(int start, int end, QFontMetricsF *&metrics, int &formatListIdx, int &formatEnd);
 
     struct ChatLinePart {
       QString plainText;
@@ -47,7 +47,7 @@ class ChatLineModelItem : public MessageModelItem {
     };
     ChatLinePart _timestamp, _sender, _contents;
 
-    WrapList _wrapList;
+    ChatLineModel::WrapList _wrapList;
 };
 
 #endif