X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fqtui%2Fchatview.cpp;h=918cec64a42bb662d0729d9fdeb005435c209344;hp=17dc074042bb6134881a2ae366671710de7528de;hb=26406e4c1f049a021fd99fd6592389289a510e99;hpb=4a5065255e652dd0c301bac0db41b7afb777ef49 diff --git a/src/qtui/chatview.cpp b/src/qtui/chatview.cpp index 17dc0740..918cec64 100644 --- a/src/qtui/chatview.cpp +++ b/src/qtui/chatview.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-2013 by the Quassel Project * + * Copyright (C) 2005-2020 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -18,52 +18,54 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include "chatview.h" + +#include + #include #include #include #include #include "bufferwidget.h" +#include "chatline.h" #include "chatscene.h" -#include "chatview.h" #include "client.h" +#include "clientignorelistmanager.h" #include "messagefilter.h" #include "qtui.h" #include "qtuistyle.h" -#include "clientignorelistmanager.h" - -#include "chatline.h" +#include "util.h" -ChatView::ChatView(BufferId bufferId, QWidget *parent) - : QGraphicsView(parent), - AbstractChatView() +ChatView::ChatView(BufferId bufferId, QWidget* parent) + : QGraphicsView(parent) + , AbstractChatView() { QList filterList; filterList.append(bufferId); - MessageFilter *filter = new MessageFilter(Client::messageModel(), filterList, this); + auto* filter = new MessageFilter(Client::messageModel(), filterList, this); init(filter); } - -ChatView::ChatView(MessageFilter *filter, QWidget *parent) - : QGraphicsView(parent), - AbstractChatView() +ChatView::ChatView(MessageFilter* filter, QWidget* parent) + : QGraphicsView(parent) + , AbstractChatView() { init(filter); } - -void ChatView::init(MessageFilter *filter) +void ChatView::init(MessageFilter* filter) { - _bufferContainer = 0; + _bufferContainer = nullptr; _currentScaleFactor = 1; _invalidateFilter = false; + setAttribute(Qt::WA_AcceptTouchEvents); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - setAlignment(Qt::AlignLeft|Qt::AlignBottom); + setAlignment(Qt::AlignLeft | Qt::AlignBottom); setInteractive(true); - //setOptimizationFlags(QGraphicsView::DontClipPainter | QGraphicsView::DontAdjustForAntialiasing); + // setOptimizationFlags(QGraphicsView::DontClipPainter | QGraphicsView::DontAdjustForAntialiasing); // setOptimizationFlags(QGraphicsView::DontAdjustForAntialiasing); setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); // setTransformationAnchor(QGraphicsView::NoAnchor); @@ -71,36 +73,37 @@ void ChatView::init(MessageFilter *filter) _scrollTimer.setInterval(100); _scrollTimer.setSingleShot(true); - connect(&_scrollTimer, SIGNAL(timeout()), SLOT(scrollTimerTimeout())); + connect(&_scrollTimer, &QTimer::timeout, this, &ChatView::scrollTimerTimeout); _scene = new ChatScene(filter, filter->idString(), viewport()->width(), this); - connect(_scene, SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(adjustSceneRect())); - connect(_scene, SIGNAL(lastLineChanged(QGraphicsItem *, qreal)), this, SLOT(lastLineChanged(QGraphicsItem *, qreal))); - connect(_scene, SIGNAL(mouseMoveWhileSelecting(const QPointF &)), this, SLOT(mouseMoveWhileSelecting(const QPointF &))); + connect(_scene, &QGraphicsScene::sceneRectChanged, this, &ChatView::adjustSceneRect); + connect(_scene, &ChatScene::lastLineChanged, this, &ChatView::lastLineChanged); + connect(_scene, &ChatScene::mouseMoveWhileSelecting, this, &ChatView::mouseMoveWhileSelecting); setScene(_scene); - connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(verticalScrollbarChanged(int))); - _lastScrollbarPos = verticalScrollBar()->value(); + connect(verticalScrollBar(), &QAbstractSlider::valueChanged, this, &ChatView::verticalScrollbarChanged); + _lastScrollbarPos = verticalScrollBar()->maximum(); + + // Workaround for the ChatView scrolling up a fair bit when scrollbar becomes visible + verticalScrollBar()->installEventFilter(this); - connect(Client::networkModel(), SIGNAL(markerLineSet(BufferId, MsgId)), SLOT(markerLineSet(BufferId, MsgId))); + connect(Client::networkModel(), &NetworkModel::markerLineSet, this, &ChatView::markerLineSet); // only connect if client is synched with a core if (Client::isConnected()) - connect(Client::ignoreListManager(), SIGNAL(ignoreListChanged()), this, SLOT(invalidateFilter())); + connect(Client::ignoreListManager(), &ClientIgnoreListManager::ignoreListChanged, this, &ChatView::invalidateFilter); } - -bool ChatView::event(QEvent *event) +bool ChatView::event(QEvent* event) { if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(event); + auto* keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_PageUp: case Qt::Key_PageDown: - if (!verticalScrollBar()->isVisible()) { - scene()->requestBacklog(); + if (requestBacklogForScroll()) { return true; } default: @@ -108,9 +111,43 @@ bool ChatView::event(QEvent *event) } } - if (event->type() == QEvent::Wheel) { - if (!verticalScrollBar()->isVisible()) { - scene()->requestBacklog(); + if (event->type() == QEvent::TouchBegin && ((QTouchEvent*)event)->device()->type() == QTouchDevice::TouchScreen) { + // Enable scrolling by draging, disable selecting/clicking content + setDragMode(QGraphicsView::ScrollHandDrag); + setInteractive(false); + // if scrollbar is not visible we need to request backlog below else we need to accept + // the event now (return true) so that we will receive TouchUpdate and TouchEnd/TouchCancel + if (verticalScrollBar()->isVisible()) + return true; + } + + if (event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) { + // End scroll and reset settings to default + setDragMode(QGraphicsView::NoDrag); + setInteractive(true); + _firstTouchUpdateHappened = false; + return true; + } + + if (event->type() == QEvent::TouchUpdate) { + if (!_firstTouchUpdateHappened) { + // After the first movement of a Touch-Point, calculate the distance in both axis + // and if the point moved more horizontally abort scroll. + QTouchEvent::TouchPoint p = ((QTouchEvent*)event)->touchPoints().at(0); + double dx = qAbs(p.lastPos().x() - p.pos().x()); + double dy = qAbs(p.lastPos().y() - p.pos().y()); + if (dx > dy) { + setDragMode(QGraphicsView::NoDrag); + setInteractive(true); + } + _firstTouchUpdateHappened = true; + } + // Applying the movement happens automatically by the drag-mode + } + if (event->type() == QEvent::Wheel + || (event->type() == QEvent::TouchBegin && ((QTouchEvent*)event)->device()->type() == QTouchDevice::TouchScreen) + || event->type() == QEvent::TouchUpdate) { + if (requestBacklogForScroll()) { return true; } } @@ -123,25 +160,51 @@ bool ChatView::event(QEvent *event) return QGraphicsView::event(event); } +bool ChatView::eventFilter(QObject* watched, QEvent* event) +{ + QAbstractSlider* vbar = verticalScrollBar(); + Q_ASSERT(vbar); + + if (watched != vbar) { + // Ignore and pass through all events not featuring the scrollbar + return false; + } + if (event->type() == QEvent::Show) { + // FIXME: Workaround for the ChatView scrolling up a fair bit when transitioning from the + // vertical scrollbar not being visible, to becoming visible. This happens especially + // often when no initial backlog is loaded. + if (_backlogRequestedBeforeScrollable) { + _backlogRequestedBeforeScrollable = false; + vbar->setValue(vbar->maximum()); + } + } + // Pass through all events + return false; +} -void ChatView::resizeEvent(QResizeEvent *event) +void ChatView::resizeEvent(QResizeEvent* event) { + // if view is currently scrolled to bottom, we want it that way after resizing + bool atBottom = (_lastScrollbarPos == verticalScrollBar()->maximum()); + QGraphicsView::resizeEvent(event); - // FIXME: do we really need to scroll down on resize? + // if scrolling to bottom, do it immediately. + if (atBottom) { + // we can reduce viewport updates if we scroll to the bottom allready at the beginning + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + } - // we can reduce viewport updates if we scroll to the bottom allready at the beginning - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); scene()->updateForViewport(viewport()->width(), viewport()->height()); adjustSceneRect(); - _lastScrollbarPos = verticalScrollBar()->maximum(); - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); - + if (atBottom) { + _lastScrollbarPos = verticalScrollBar()->maximum(); + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + } checkChatLineCaches(); } - void ChatView::adjustSceneRect() { // Workaround for QTBUG-6322 @@ -154,8 +217,7 @@ void ChatView::adjustSceneRect() setSceneRect(scene()->sceneRect().adjusted(0, 0, -25, 0)); } - -void ChatView::mouseMoveWhileSelecting(const QPointF &scenePos) +void ChatView::mouseMoveWhileSelecting(const QPointF& scenePos) { int y = (int)mapFromScene(scenePos).y(); _scrollOffset = 0; @@ -168,36 +230,33 @@ void ChatView::mouseMoveWhileSelecting(const QPointF &scenePos) _scrollTimer.start(); } - void ChatView::scrollTimerTimeout() { // scroll view - QAbstractSlider *vbar = verticalScrollBar(); + QAbstractSlider* vbar = verticalScrollBar(); if (_scrollOffset < 0 && vbar->value() > 0) vbar->setValue(qMax(vbar->value() + _scrollOffset, 0)); else if (_scrollOffset > 0 && vbar->value() < vbar->maximum()) vbar->setValue(qMin(vbar->value() + _scrollOffset, vbar->maximum())); } - -void ChatView::lastLineChanged(QGraphicsItem *chatLine, qreal offset) +void ChatView::lastLineChanged(QGraphicsItem* chatLine, qreal offset) { Q_UNUSED(chatLine) // disabled until further testing/discussion - //if(!scene()->isScrollingAllowed()) + // if(!scene()->isScrollingAllowed()) // return; - QAbstractSlider *vbar = verticalScrollBar(); + QAbstractSlider* vbar = verticalScrollBar(); Q_ASSERT(vbar); - if (vbar->maximum() - vbar->value() <= (offset + 5) * _currentScaleFactor) { // 5px grace area + if (vbar->maximum() - vbar->value() <= (offset + 5) * _currentScaleFactor) { // 5px grace area vbar->setValue(vbar->maximum()); } } - void ChatView::verticalScrollbarChanged(int newPos) { - QAbstractSlider *vbar = verticalScrollBar(); + QAbstractSlider* vbar = verticalScrollBar(); Q_ASSERT(vbar); // check for backlog request @@ -217,71 +276,65 @@ void ChatView::verticalScrollbarChanged(int newPos) vbar->setValue(vbar->maximum()); } - MsgId ChatView::lastMsgId() const { if (!scene()) - return MsgId(); + return {}; - QAbstractItemModel *model = scene()->model(); + QAbstractItemModel* model = scene()->model(); if (!model || model->rowCount() == 0) - return MsgId(); + return {}; return model->index(model->rowCount() - 1, 0).data(MessageModel::MsgIdRole).value(); } - MsgId ChatView::lastVisibleMsgId() const { - ChatLine *line = lastVisibleChatLine(); + ChatLine* line = lastVisibleChatLine(); if (line) return line->msgId(); - return MsgId(); + return {}; } - -bool chatLinePtrLessThan(ChatLine *one, ChatLine *other) +bool chatLinePtrLessThan(ChatLine* one, ChatLine* other) { return one->row() < other->row(); } - // TODO: figure out if it's cheaper to use a cached list (that we'd need to keep updated) -QSet ChatView::visibleChatLines(Qt::ItemSelectionMode mode) const +QSet ChatView::visibleChatLines(Qt::ItemSelectionMode mode) const { - QSet result; - foreach(QGraphicsItem *item, items(viewport()->rect().adjusted(-1, -1, 1, 1), mode)) { - ChatLine *line = qgraphicsitem_cast(item); + QSet result; + foreach (QGraphicsItem* item, items(viewport()->rect().adjusted(-1, -1, 1, 1), mode)) { + auto* line = qgraphicsitem_cast(item); if (line) result.insert(line); } return result; } - -QList ChatView::visibleChatLinesSorted(Qt::ItemSelectionMode mode) const +QList ChatView::visibleChatLinesSorted(Qt::ItemSelectionMode mode) const { - QList result = visibleChatLines(mode).toList(); - qSort(result.begin(), result.end(), chatLinePtrLessThan); + QList result = visibleChatLines(mode).values(); + std::sort(result.begin(), result.end(), chatLinePtrLessThan); return result; } - -ChatLine *ChatView::lastVisibleChatLine(bool ignoreDayChange) const +ChatLine* ChatView::lastVisibleChatLine(bool ignoreDayChange) const { if (!scene()) - return 0; + return nullptr; - QAbstractItemModel *model = scene()->model(); + QAbstractItemModel* model = scene()->model(); if (!model || model->rowCount() == 0) - return 0; + return nullptr; int row = -1; - QSet visibleLines = visibleChatLines(Qt::ContainsItemBoundingRect); - foreach(ChatLine *line, visibleLines) { + QSet visibleLines = visibleChatLines(Qt::ContainsItemBoundingRect); + foreach (ChatLine* line, visibleLines) { if (line->row() > row && (ignoreDayChange ? line->msgType() != Message::DayChange : true)) row = line->row(); } @@ -289,16 +342,14 @@ ChatLine *ChatView::lastVisibleChatLine(bool ignoreDayChange) const if (row >= 0) return scene()->chatLine(row); - return 0; + return nullptr; } - void ChatView::setMarkerLineVisible(bool visible) { scene()->setMarkerLineVisible(visible); } - void ChatView::setMarkerLine(MsgId msgId) { if (!scene()->isSingleBufferScene()) @@ -308,7 +359,6 @@ void ChatView::setMarkerLine(MsgId msgId) Client::setMarkerLine(bufId, msgId); } - void ChatView::markerLineSet(BufferId buffer, MsgId msgId) { if (!scene()->isSingleBufferScene() || scene()->singleBufferId() != buffer) @@ -318,24 +368,21 @@ void ChatView::markerLineSet(BufferId buffer, MsgId msgId) scene()->setMarkerLineVisible(true); } - void ChatView::jumpToMarkerLine(bool requestBacklog) { scene()->jumpToMarkerLine(requestBacklog); } - -void ChatView::addActionsToMenu(QMenu *menu, const QPointF &pos) +void ChatView::addActionsToMenu(QMenu* menu, const QPointF& pos) { // zoom actions - BufferWidget *bw = qobject_cast(bufferContainer()); + auto* bw = qobject_cast(bufferContainer()); if (bw) { bw->addActionsToMenu(menu, pos); menu->addSeparator(); } } - void ChatView::zoomIn() { _currentScaleFactor *= 1.2; @@ -343,7 +390,6 @@ void ChatView::zoomIn() scene()->setWidth(viewport()->width() / _currentScaleFactor - 2); } - void ChatView::zoomOut() { _currentScaleFactor /= 1.2; @@ -351,15 +397,13 @@ void ChatView::zoomOut() scene()->setWidth(viewport()->width() / _currentScaleFactor - 2); } - void ChatView::zoomOriginal() { - scale(1/_currentScaleFactor, 1/_currentScaleFactor); + scale(1 / _currentScaleFactor, 1 / _currentScaleFactor); _currentScaleFactor = 1; scene()->setWidth(viewport()->width() - 2); } - void ChatView::invalidateFilter() { // if this is the currently selected chatview @@ -374,15 +418,13 @@ void ChatView::invalidateFilter() } } - void ChatView::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); checkChatLineCaches(); } - -void ChatView::setHasCache(ChatLine *line, bool hasCache) +void ChatView::setHasCache(ChatLine* line, bool hasCache) { if (hasCache) _linesWithCache.insert(line); @@ -390,14 +432,31 @@ void ChatView::setHasCache(ChatLine *line, bool hasCache) _linesWithCache.remove(line); } +bool ChatView::requestBacklogForScroll() +{ + if (!verticalScrollBar()->isVisible()) { + // Not able to scroll, fetch backlog + // + // Future improvement: continue fetching backlog in chunks until the scrollbar is visible, + // or the beginning of the buffer has been reached. + scene()->requestBacklog(); + _backlogRequestedBeforeScrollable = true; + // Backlog has been requested + return true; + } + else { + // Scrollbar already visible, no backlog requested + return false; + } +} void ChatView::checkChatLineCaches() { - qreal top = mapToScene(viewport()->rect().topLeft()).y() - 10; // some grace area to avoid premature cleaning + qreal top = mapToScene(viewport()->rect().topLeft()).y() - 10; // some grace area to avoid premature cleaning qreal bottom = mapToScene(viewport()->rect().bottomRight()).y() + 10; - QSet::iterator iter = _linesWithCache.begin(); + QSet::iterator iter = _linesWithCache.begin(); while (iter != _linesWithCache.end()) { - ChatLine *line = *iter; + ChatLine* line = *iter; if (line->pos().y() + line->height() < top || line->pos().y() > bottom) { line->clearCache(); iter = _linesWithCache.erase(iter);