Pimp my ChatView!
authorManuel Nickschas <sputnick@quassel-irc.org>
Mon, 27 Oct 2008 19:45:38 +0000 (20:45 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 28 Oct 2008 00:26:04 +0000 (01:26 +0100)
Sender items now fade out with a little gradient rather than being cut off hard if they don't fit.
Also, ChatScene::setSenderCutoffMode(Cutoff[Left|Right]) allows setting how/where oversized senders
are to be cutoff. Default is now the 0.2 behavior, as requested by users.
We still lack a config UI for this.

src/qtui/chatitem.cpp
src/qtui/chatitem.h
src/qtui/chatscene.cpp
src/qtui/chatscene.h

index 21829c2..585c958 100644 (file)
@@ -101,25 +101,9 @@ ChatItemPrivate *ChatItem::privateData() const {
 void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
   Q_UNUSED(option); Q_UNUSED(widget);
   painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
 void ChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
   Q_UNUSED(option); Q_UNUSED(widget);
   painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
-  //if(_selectionMode == FullSelection) {
-    //painter->save();
-    //painter->fillRect(boundingRect(), QApplication::palette().brush(QPalette::Highlight));
-    //painter->restore();
-  //}
   QVector<QTextLayout::FormatRange> formats = additionalFormats();
   QVector<QTextLayout::FormatRange> formats = additionalFormats();
-  if(_selectionMode != NoSelection) {
-    QTextLayout::FormatRange selectFmt;
-    selectFmt.format.setForeground(QApplication::palette().brush(QPalette::HighlightedText));
-    selectFmt.format.setBackground(QApplication::palette().brush(QPalette::Highlight));
-    if(_selectionMode == PartialSelection) {
-      selectFmt.start = qMin(_selectionStart, _selectionEnd);
-      selectFmt.length = qAbs(_selectionStart - _selectionEnd);
-    } else { // FullSelection
-      selectFmt.start = 0;
-      selectFmt.length = data(MessageModel::DisplayRole).toString().length();
-    }
-    formats.append(selectFmt);
-  }
+  QTextLayout::FormatRange selectFmt = selectionFormat();
+  if(selectFmt.format.isValid()) formats.append(selectFmt);
   layout()->draw(painter, QPointF(0,0), formats, boundingRect());
 
   // Debuging Stuff
   layout()->draw(painter, QPointF(0,0), formats, boundingRect());
 
   // Debuging Stuff
@@ -180,6 +164,25 @@ void ChatItem::continueSelecting(const QPointF &pos) {
   update();
 }
 
   update();
 }
 
+QTextLayout::FormatRange ChatItem::selectionFormat() const {
+  QTextLayout::FormatRange selectFmt;
+  if(_selectionMode != NoSelection) {
+    selectFmt.format.setForeground(QApplication::palette().brush(QPalette::HighlightedText));
+    selectFmt.format.setBackground(QApplication::palette().brush(QPalette::Highlight));
+    if(_selectionMode == PartialSelection) {
+      selectFmt.start = qMin(_selectionStart, _selectionEnd);
+      selectFmt.length = qAbs(_selectionStart - _selectionEnd);
+    } else { // FullSelection
+      selectFmt.start = 0;
+      selectFmt.length = data(MessageModel::DisplayRole).toString().length();
+    }
+  } else {
+    selectFmt.start = -1;
+    selectFmt.length = 0;
+  }
+  return selectFmt;
+}
+
 QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive) {
   QList<QRectF> resultList;
   const QAbstractItemModel *model_ = model();
 QList<QRectF> ChatItem::findWords(const QString &searchWord, Qt::CaseSensitivity caseSensitive) {
   QList<QRectF> resultList;
   const QAbstractItemModel *model_ = model();
@@ -257,6 +260,51 @@ void ChatItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
 // SenderChatItem
 // ************************************************************
 
 // SenderChatItem
 // ************************************************************
 
+void SenderChatItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
+  Q_UNUSED(option); Q_UNUSED(widget);
+
+  //painter->setClipRect(boundingRect()); // no idea why QGraphicsItem clipping won't work
+  qreal layoutWidth = layout()->minimumWidth();
+  qreal offset = 0;
+  if(chatScene()->senderCutoffMode() == ChatScene::CutoffLeft)
+    offset = qMin(width() - layoutWidth, (qreal)0);
+  else
+    offset = qMax(layoutWidth - width(), (qreal)0);
+
+  QTextLayout::FormatRange selectFmt = selectionFormat();
+
+  if(layoutWidth > width()) {
+    // Draw a nice gradient for longer items
+    // Qt's text drawing with a gradient brush sucks, so we use an alpha-channeled pixmap instead
+    QPixmap pixmap(QSize(layout()->boundingRect().width(), layout()->boundingRect().height()));
+    pixmap.fill(QApplication::palette().brush(QPalette::Base).color());
+    QPainter pixPainter(&pixmap);
+    layout()->draw(&pixPainter, QPointF(qMax(offset, (qreal)0), 0), QVector<QTextLayout::FormatRange>() << selectFmt);
+    pixPainter.end();
+
+    // Create alpha channel mask
+    QPixmap mask(pixmap.size());
+    QPainter maskPainter(&mask);
+    QLinearGradient gradient;
+    if(offset < 0) {
+      gradient.setStart(0, 0);
+      gradient.setFinalStop(12, 0);
+      gradient.setColorAt(0, Qt::black);
+      gradient.setColorAt(1, Qt::white);
+    } else {
+      gradient.setStart(width()-10, 0);
+      gradient.setFinalStop(width(), 0);
+      gradient.setColorAt(0, Qt::white);
+      gradient.setColorAt(1, Qt::black);
+    }
+    maskPainter.fillRect(boundingRect(), gradient);
+    pixmap.setAlphaChannel(mask);
+    painter->drawPixmap(0, 0, pixmap);
+  } else {
+    layout()->draw(painter, QPointF(0,0), QVector<QTextLayout::FormatRange>() << selectFmt, boundingRect());
+  }
+}
+
 // ************************************************************
 // ContentsChatItem
 // ************************************************************
 // ************************************************************
 // ContentsChatItem
 // ************************************************************
index 3ed2cee..1f61fff 100644 (file)
@@ -69,7 +69,9 @@ protected:
 
   inline QTextLayout *layout() const;
 
 
   inline QTextLayout *layout() const;
 
+  virtual QTextLayout::FormatRange selectionFormat() const;
   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
   virtual inline QVector<QTextLayout::FormatRange> additionalFormats() const { return QVector<QTextLayout::FormatRange>(); }
+
   qint16 posToCursor(const QPointF &pos);
 
   inline bool hasPrivateData() const { return (bool)_data; }
   qint16 posToCursor(const QPointF &pos);
 
   inline bool hasPrivateData() const { return (bool)_data; }
@@ -137,7 +139,8 @@ public:
   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
 
 protected:
   virtual inline ChatLineModel::ColumnType column() const { return ChatLineModel::SenderColumn; }
 
 protected:
-  virtual inline ChatItemPrivate *newPrivateData() { return new ChatItemPrivate(createLayout(QTextOption::WrapAnywhere, Qt::AlignRight)); }
+  virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
+  virtual inline ChatItemPrivate *newPrivateData() { return new ChatItemPrivate(createLayout(QTextOption::ManualWrap, Qt::AlignRight)); }
 };
 
 // ************************************************************
 };
 
 // ************************************************************
@@ -207,7 +210,8 @@ struct ContentsChatItemPrivate : ChatItemPrivate {
   ContentsChatItem::Clickable currentClickable;
   bool hasDragged;
 
   ContentsChatItem::Clickable currentClickable;
   bool hasDragged;
 
-  ContentsChatItemPrivate(QTextLayout *l, const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent) : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {}
+  ContentsChatItemPrivate(QTextLayout *l, const QList<ContentsChatItem::Clickable> &c, ContentsChatItem *parent)
+  : ChatItemPrivate(l), contentsItem(parent), clickables(c), hasDragged(false) {}
 };
 
 //inlines regarding ContentsChatItemPrivate
 };
 
 //inlines regarding ContentsChatItemPrivate
index 811eec3..b4cb786 100644 (file)
@@ -47,6 +47,7 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w
     _sceneRect(0, 0, width, 0),
     _firstLineRow(-1),
     _viewportHeight(0),
     _sceneRect(0, 0, width, 0),
     _firstLineRow(-1),
     _viewportHeight(0),
+    _cutoffMode(CutoffLeft),
     _selectingItem(0),
     _selectionStart(-1),
     _isSelecting(false)
     _selectingItem(0),
     _selectionStart(-1),
     _isSelecting(false)
index 753b887..8f56d62 100644 (file)
@@ -39,6 +39,11 @@ class ChatScene : public QGraphicsScene {
   Q_OBJECT
 
 public:
   Q_OBJECT
 
 public:
+  enum CutoffMode {
+    CutoffLeft,
+    CutoffRight
+  };
+
   ChatScene(QAbstractItemModel *model, const QString &idString, qreal width, QObject *parent);
   virtual ~ChatScene();
 
   ChatScene(QAbstractItemModel *model, const QString &idString, qreal width, QObject *parent);
   virtual ~ChatScene();
 
@@ -54,6 +59,9 @@ public:
   inline ColumnHandleItem *firstColumnHandle() const { return _firstColHandle; }
   inline ColumnHandleItem *secondColumnHandle() const { return _secondColHandle; }
 
   inline ColumnHandleItem *firstColumnHandle() const { return _firstColHandle; }
   inline ColumnHandleItem *secondColumnHandle() const { return _secondColHandle; }
 
+  inline CutoffMode senderCutoffMode() const { return _cutoffMode; }
+  inline void setSenderCutoffMode(CutoffMode mode) { _cutoffMode = mode; }
+
 public slots:
   void updateForViewport(qreal width, qreal height);
   void setWidth(qreal width);
 public slots:
   void updateForViewport(qreal width, qreal height);
   void setWidth(qreal width);
@@ -109,6 +117,7 @@ private:
 
   ColumnHandleItem *_firstColHandle, *_secondColHandle;
   qreal _firstColHandlePos, _secondColHandlePos;
 
   ColumnHandleItem *_firstColHandle, *_secondColHandle;
   qreal _firstColHandlePos, _secondColHandlePos;
+  CutoffMode _cutoffMode;
 
   ChatItem *_selectingItem;
   int _selectionStartCol, _selectionMinCol;
 
   ChatItem *_selectingItem;
   int _selectionStartCol, _selectionMinCol;