From 6e73ba4a19fd92038e1ea749125767661fb34e27 Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Wed, 16 Jun 2010 17:21:21 +0200 Subject: [PATCH] Refactor the markerline into a proper QGraphicsWidget Unitl now, ChatLines would check if they're the one supposed to display the markerline for every paintEvent, and the one appropriate would draw it then. Besides being inefficient, this also caused trouble with the more flexible markerline stuff introduced recently. Now the markerline is a proper QGraphicsWidget. It can be fully styled via a brush property: Palette { marker-line: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop:0 red, stop: 0.1 transparent); } This lets it look identical to the "old" markerline and is hence the default now. Note that the height of the markerline object is equal to a single line of text (so this is the area you get to play in), unless the brush is set to a solid color, in which case it's a simple line 1px wide. This makes it look acceptable when using an older stylesheet. This commit also fixes a crash introduced with the markerline rewrite a few weeks ago. --- src/qtui/CMakeLists.txt | 2 ++ src/qtui/bufferwidget.cpp | 7 ++-- src/qtui/bufferwidget.h | 5 +-- src/qtui/chatline.cpp | 19 ---------- src/qtui/chatscene.cpp | 72 +++++++++++++++++++++++++++++++++++-- src/qtui/chatscene.h | 20 ++++++++--- src/qtui/chatview.cpp | 35 ++++-------------- src/qtui/chatview.h | 7 +--- src/qtui/markerlineitem.cpp | 59 ++++++++++++++++++++++++++++++ src/qtui/markerlineitem.h | 50 ++++++++++++++++++++++++++ 10 files changed, 211 insertions(+), 65 deletions(-) create mode 100644 src/qtui/markerlineitem.cpp create mode 100644 src/qtui/markerlineitem.h diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index fb74978b..46eb704e 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES legacysystemtray.cpp mainpage.cpp mainwin.cpp + markerlineitem.cpp msgprocessorstatuswidget.cpp nicklistwidget.cpp qtui.cpp @@ -86,6 +87,7 @@ set(MOC_HDRS legacysystemtray.h mainpage.h mainwin.h + markerlineitem.h msgprocessorstatuswidget.h nicklistwidget.h qtui.h diff --git a/src/qtui/bufferwidget.cpp b/src/qtui/bufferwidget.cpp index ab068997..047759eb 100644 --- a/src/qtui/bufferwidget.cpp +++ b/src/qtui/bufferwidget.cpp @@ -238,14 +238,15 @@ void BufferWidget::setMarkerLine(ChatView *view, bool allowGoingBack) { ChatLine *lastLine = view->lastVisibleChatLine(); if(lastLine) { + QModelIndex idx = lastLine->index(); + MsgId msgId = idx.data(MessageModel::MsgIdRole).value(); + if(!allowGoingBack) { - QModelIndex idx = lastLine->index(); BufferId bufId = view->scene()->singleBufferId(); - MsgId msgId = idx.data(MessageModel::MsgIdRole).value(); MsgId oldMsgId = Client::markerLine(bufId); if(oldMsgId.isValid() && msgId <= oldMsgId) return; } - view->setMarkedLine(lastLine); + view->setMarkerLine(msgId); } } diff --git a/src/qtui/bufferwidget.h b/src/qtui/bufferwidget.h index dc8b8236..b818435a 100644 --- a/src/qtui/bufferwidget.h +++ b/src/qtui/bufferwidget.h @@ -42,6 +42,9 @@ public: inline ChatViewSearchBar *searchBar() const { return ui.searchBar; } void addActionsToMenu(QMenu *, const QPointF &pos); +public slots: + virtual void setMarkerLine(ChatView *view = 0, bool allowGoingBack = true); + protected: virtual AbstractChatView *createChatView(BufferId); virtual void removeChatView(BufferId); @@ -50,8 +53,6 @@ protected: protected slots: virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); virtual void showChatView(BufferId); -public slots: - virtual void setMarkerLine(ChatView *view = 0, bool allowGoingBack = true); private slots: void scrollToHighlight(QGraphicsItem *highlightItem); diff --git a/src/qtui/chatline.cpp b/src/qtui/chatline.cpp index 99e09829..4f03794e 100644 --- a/src/qtui/chatline.cpp +++ b/src/qtui/chatline.cpp @@ -209,25 +209,6 @@ void ChatLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, timestampItem()->paint(painter, option, widget); senderItem()->paint(painter, option, widget); contentsItem()->paint(painter, option, widget); - - // new line marker - if(model_ && row() > 0 && chatScene()->isSingleBufferScene()) { - QModelIndex prevRowIdx = model_->index(row() - 1, 0); - MsgId prevMsgId = prevRowIdx.data(MessageModel::MsgIdRole).value(); - MsgId myMsgId = myIdx.data(MessageModel::MsgIdRole).value(); - Message::Flags flags = (Message::Flags)myIdx.data(MessageModel::FlagsRole).toInt(); - - if(chatView()->isMarkerLineVisible()) { - BufferId bufferId = BufferId(chatScene()->idString().toInt()); - MsgId lastSeenMsgId = Client::networkModel()->markerLineMsgId(bufferId); - if(lastSeenMsgId < myMsgId && lastSeenMsgId >= prevMsgId) { - QLinearGradient gradient(0, 0, 0, contentsItem()->fontMetrics()->lineSpacing()); - gradient.setColorAt(0, QtUi::style()->brush(UiStyle::MarkerLine).color()); // FIXME: Use full (gradient?) brush instead of just the color - gradient.setColorAt(0.1, Qt::transparent); - painter->fillRect(boundingRect(), gradient); - } - } - } } // We need to dispatch all mouse-related events to the appropriate (mouse grabbing) ChatItem diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index cb5b3f70..46cfc3eb 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -47,6 +47,7 @@ #include "contextmenuactionprovider.h" #include "iconloader.h" #include "mainwin.h" +#include "markerlineitem.h" #include "messagefilter.h" #include "qtui.h" #include "qtuistyle.h" @@ -64,6 +65,9 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w _sceneRect(0, 0, width, 0), _firstLineRow(-1), _viewportHeight(0), + _markerLine(new MarkerLineItem(width)), + _markerLineValid(false), + _markerLineVisible(false), _cutoffMode(CutoffRight), _selectingItem(0), _selectionStart(-1), @@ -77,6 +81,9 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w _singleBufferId = filter->singleBufferId(); } + addItem(_markerLine); + connect(this, SIGNAL(sceneRectChanged(const QRectF &)), _markerLine, SLOT(sceneRectChanged(const QRectF &))); + ChatViewSettings defaultSettings; int defaultFirstColHandlePos = defaultSettings.value("FirstColumnHandlePos", 80).toInt(); int defaultSecondColHandlePos = defaultSettings.value("SecondColumnHandlePos", 200).toInt(); @@ -107,6 +114,8 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w this, SLOT(rowsInserted(const QModelIndex &, int, int))); connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved())); connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(dataChanged(QModelIndex, QModelIndex))); #ifdef HAVE_WEBKIT @@ -138,7 +147,7 @@ ColumnHandleItem *ChatScene::secondColumnHandle() const { return _secondColHandle; } -ChatLine *ChatScene::chatLine(MsgId msgId) const { +ChatLine *ChatScene::chatLine(MsgId msgId, bool matchExact) const { if(!_lines.count()) return 0; @@ -159,10 +168,22 @@ ChatLine *ChatScene::chatLine(MsgId msgId) const { n = half; } } - if((*start)->msgId() == msgId) + + if(start != end && (*start)->msgId() == msgId) return *start; - return 0; + if(matchExact) + return 0; + + // if we didn't find the exact msgId, take the next-lower one (this makes sense for lastSeen) + if(start == end) // higher than last element + return _lines.last(); + + if(start == _lines.begin()) // not (yet?) in our scene + return 0; + + // return the next-lower line + return *(--start); } ChatItem *ChatScene::chatItemAt(const QPointF &scenePos) const { @@ -180,6 +201,35 @@ bool ChatScene::containsBuffer(const BufferId &id) const { return false; } +void ChatScene::setMarkerLineVisible(bool visible) { + _markerLineVisible = visible; + if(visible && _markerLineValid) + _markerLine->setVisible(true); + else + _markerLine->setVisible(false); +} + +void ChatScene::setMarkerLine(MsgId msgId) { + if(msgId.isValid()) { + ChatLine *line = chatLine(msgId, false); + if(line) { + // if this was the last line, we won't see it because it's outside the sceneRect + // .. which is exactly what we want :) + _markerLine->setPos(line->pos() + QPointF(0, line->height())); + + // DayChange messages might have been hidden outside the scene rect, don't make the markerline visible then! + if(_markerLine->pos().y() >= sceneRect().y()) { + _markerLineValid = true; + if(_markerLineVisible) + _markerLine->setVisible(true); + return; + } + } + } + _markerLineValid = false; + _markerLine->setVisible(false); +} + void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) { Q_UNUSED(index); @@ -310,6 +360,14 @@ void ChatScene::rowsInserted(const QModelIndex &index, int start, int end) { if(atBottom) { emit lastLineChanged(_lines.last(), h); } + + // now move the marker line if necessary. we don't need to do anything if we appended lines though... + if(isSingleBufferScene()) { + if(!_markerLineValid || !atBottom) { + MsgId msgId = Client::markerLine(singleBufferId()); + setMarkerLine(msgId); + } + } } void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { @@ -403,6 +461,14 @@ void ChatScene::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e updateSceneRect(); } +void ChatScene::rowsRemoved() { + // move the marker line if necessary + if(isSingleBufferScene()) { + MsgId msgId = Client::markerLine(singleBufferId()); + setMarkerLine(msgId); + } +} + void ChatScene::dataChanged(const QModelIndex &tl, const QModelIndex &br) { layout(tl.row(), br.row(), _sceneRect.width()); } diff --git a/src/qtui/chatscene.h b/src/qtui/chatscene.h index 3daf861b..c4a8b015 100644 --- a/src/qtui/chatscene.h +++ b/src/qtui/chatscene.h @@ -36,6 +36,7 @@ class ChatItem; class ChatLine; class ChatView; class ColumnHandleItem; +class MarkerLineItem; class WebPreviewItem; class QGraphicsSceneMouseEvent; @@ -57,7 +58,8 @@ public: ContentsChatItemType, SearchHighlightType, WebPreviewType, - ColumnHandleType + ColumnHandleType, + MarkerLineType }; enum ClickMode { @@ -88,11 +90,13 @@ public: //! Find the ChatLine belonging to a MsgId /** Searches for the ChatLine belonging to a MsgId. * Note that this method performs a binary search, hence it has as complexity of O(log n). - * If there is more than one ChatLine for the given ID, the first one will be returned. - * \param msgId The message ID to look for + * If matchExact is false, and we don't have an exact match for the given msgId, we return the visible line right + * above the requested one. + * \param msgId The message ID to look for + * \param matchExact Whether we find only exact matches * \return The ChatLine corresponding to the given MsgId */ - ChatLine *chatLine(MsgId msgId) const; + ChatLine *chatLine(MsgId msgId, bool matchExact = true) const; inline ChatLine *lastLine() const { return _lines.count() ? _lines.last() : 0; } @@ -122,6 +126,9 @@ public: void setWidth(qreal width); void layout(int start, int end, qreal width); + void setMarkerLineVisible(bool visible = true); + void setMarkerLine(MsgId msgId); + // these are used by the chatitems to notify the scene and manage selections void setSelectingItem(ChatItem *item); ChatItem *selectingItem() const { return _selectingItem; } @@ -164,6 +171,8 @@ private slots: #endif void showWebPreviewChanged(); + void rowsRemoved(); + void clickTimeout(); private: @@ -185,6 +194,9 @@ private: void updateSceneRect(const QRectF &rect); qreal _viewportHeight; + MarkerLineItem *_markerLine; + bool _markerLineValid, _markerLineVisible; + ColumnHandleItem *_firstColHandle, *_secondColHandle; qreal _firstColHandlePos, _secondColHandlePos; CutoffMode _cutoffMode; diff --git a/src/qtui/chatview.cpp b/src/qtui/chatview.cpp index e61dc9e8..96d7ee2f 100644 --- a/src/qtui/chatview.cpp +++ b/src/qtui/chatview.cpp @@ -55,8 +55,6 @@ void ChatView::init(MessageFilter *filter) { _bufferContainer = 0; _currentScaleFactor = 1; _invalidateFilter = false; - _markerLineVisible = true; - _markedLine = 0; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -266,42 +264,23 @@ ChatLine *ChatView::lastVisibleChatLine() const { } void ChatView::setMarkerLineVisible(bool visible) { - if(visible != _markerLineVisible) { - _markerLineVisible = visible; - } + scene()->setMarkerLineVisible(visible); } -void ChatView::setMarkedLine(ChatLine *line) { - if(_markedLine == line) - return; - +void ChatView::setMarkerLine(MsgId msgId) { if(!scene()->isSingleBufferScene()) return; - if(line) { - BufferId bufId = scene()->singleBufferId(); - Client::setMarkerLine(bufId, line->msgId()); - } + BufferId bufId = scene()->singleBufferId(); + Client::setMarkerLine(bufId, msgId); } -void ChatView::markerLineSet(BufferId buffer, MsgId msg) { +void ChatView::markerLineSet(BufferId buffer, MsgId msgId) { if(!scene()->isSingleBufferScene() || scene()->singleBufferId() != buffer) return; - ChatLine *newLine = scene()->chatLine(msg); - if(_markedLine == newLine) - return; - - ChatLine *oldLine = _markedLine; - _markedLine = newLine; - - if(oldLine) - oldLine->update(); - - if(newLine) { - setMarkerLineVisible(true); - newLine->update(); - } + scene()->setMarkerLine(msgId); + scene()->setMarkerLineVisible(true); } void ChatView::addActionsToMenu(QMenu *menu, const QPointF &pos) { diff --git a/src/qtui/chatview.h b/src/qtui/chatview.h index 02655675..c2aa6040 100644 --- a/src/qtui/chatview.h +++ b/src/qtui/chatview.h @@ -69,9 +69,6 @@ public: virtual void addActionsToMenu(QMenu *, const QPointF &pos); - inline bool isMarkerLineVisible() const { return _markerLineVisible; } - inline ChatLine *markedLine() const { return _markedLine; } - //! Tell the view that this ChatLine has cached data /** ChatLines cache some layout data that should be cleared as soon as it's no * longer visible. A ChatLine caching data registers itself with this method to @@ -88,7 +85,7 @@ public slots: void zoomOriginal(); void setMarkerLineVisible(bool visible = true); - void setMarkedLine(ChatLine *line); + void setMarkerLine(MsgId msgId); protected: virtual bool event(QEvent *event); @@ -117,8 +114,6 @@ private: QTimer _scrollTimer; int _scrollOffset; bool _invalidateFilter; - bool _markerLineVisible; - ChatLine *_markedLine; QSet _linesWithCache; }; diff --git a/src/qtui/markerlineitem.cpp b/src/qtui/markerlineitem.cpp new file mode 100644 index 00000000..9782a793 --- /dev/null +++ b/src/qtui/markerlineitem.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2010 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include + +#include "markerlineitem.h" +#include "qtui.h" + +MarkerLineItem::MarkerLineItem(qreal sceneWidth, QGraphicsItem *parent) + : QGraphicsObject(parent), + _boundingRect(0, 0, sceneWidth, 1) +{ + setZValue(8); + styleChanged(); // init brush and height + connect(QtUi::style(), SIGNAL(changed()), SLOT(styleChanged())); +} + +void MarkerLineItem::styleChanged() { + _brush = QtUi::style()->brush(UiStyle::MarkerLine); + + // if this is a solid color, we assume 1px because wesurely don't surely don't want to fill the entire chatline. + // else, use the height of a single line of text to play around with gradients etc. + qreal height = 1.; + if(_brush.style() != Qt::SolidPattern) + height = QtUi::style()->fontMetrics(QtUiStyle::PlainMsg, 0)->lineSpacing(); + + + prepareGeometryChange(); + _boundingRect = QRectF(0, 0, scene()? scene()->width() : 100, height); +} + +void MarkerLineItem::sceneRectChanged(const QRectF &rect) { + prepareGeometryChange(); + _boundingRect.setWidth(rect.width()); +} + +void MarkerLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + Q_UNUSED(option); + Q_UNUSED(widget); + + painter->fillRect(boundingRect(), _brush); +} diff --git a/src/qtui/markerlineitem.h b/src/qtui/markerlineitem.h new file mode 100644 index 00000000..3911da8a --- /dev/null +++ b/src/qtui/markerlineitem.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2010 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef MARKERLINEITEM_H_ +#define MARKERLINEITEM_H_ + +#include + +#include "chatscene.h" + +class MarkerLineItem : public QGraphicsObject { + Q_OBJECT + +public: + MarkerLineItem(qreal sceneWidth, QGraphicsItem *parent = 0); + virtual inline int type() const { return ChatScene::MarkerLineType; } + + inline QRectF boundingRect() const { return _boundingRect; } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + +public slots: + void sceneRectChanged(const QRectF &); + +private slots: + void styleChanged(); + +private: + QRectF _boundingRect; + QBrush _brush; +}; + +#endif -- 2.20.1