From 6e05782fb79e593c082aada6aab026f39dd5e49b Mon Sep 17 00:00:00 2001 From: krepa098 Date: Sun, 25 Jan 2015 14:06:06 +0100 Subject: [PATCH] ChatLog: busy-screen during resize Gets rid of most ugly hacks --- src/chatlog/chatlog.cpp | 213 +++++++++++++--------------- src/chatlog/chatlog.h | 12 +- src/chatlog/chatmessage.cpp | 13 +- src/chatlog/chatmessage.h | 1 + src/widget/form/genericchatform.cpp | 1 + 5 files changed, 120 insertions(+), 120 deletions(-) diff --git a/src/chatlog/chatlog.cpp b/src/chatlog/chatlog.cpp index a57a19898..c95fec078 100644 --- a/src/chatlog/chatlog.cpp +++ b/src/chatlog/chatlog.cpp @@ -40,6 +40,7 @@ ChatLog::ChatLog(QWidget* parent) : QGraphicsView(parent) { // Create the scene + busyScene = new QGraphicsScene(this); scene = new QGraphicsScene(this); scene->setItemIndexMethod(QGraphicsScene::NoIndex); //Bsp-Tree is actually slower in this case setScene(scene); @@ -80,43 +81,7 @@ ChatLog::ChatLog(QWidget* parent) workerTimer = new QTimer(this); workerTimer->setSingleShot(false); workerTimer->setInterval(100); - connect(workerTimer, &QTimer::timeout, this, [this] { - const int stepSize = 400; - - workerDy += layout(workerLastIndex, workerLastIndex+stepSize, useableWidth()); - - if(!visibleLines.isEmpty()) - { - int firstVisLineIndex = visibleLines.first()->getRow(); - int delta = firstVisLineIndex - workerLastIndex; - if(delta > 0 && delta < stepSize) - { - workerLastIndex += delta+1; - - if(!workerStb) - verticalScrollBar()->setValue(verticalScrollBar()->value() - workerDy); - - workerDy = 0.0; - checkVisibility(); - } - else - { - workerLastIndex += stepSize; - } - } - else - workerLastIndex += stepSize; - - if(workerLastIndex >= lines.size()) - { - workerTimer->stop(); - workerLastIndex = 0; - workerDy = 0.0; - - if(stickToBottom()) - scrollToBottom(); - } - }); + connect(workerTimer, &QTimer::timeout, this, &ChatLog::onWorkerTimeout); } void ChatLog::clearSelection() @@ -147,10 +112,10 @@ void ChatLog::updateSceneRect() setSceneRect(calculateSceneRect()); } -qreal ChatLog::layout(int start, int end, qreal width) +void ChatLog::layout(int start, int end, qreal width) { if(lines.empty()) - return false; + return; qreal h = 0.0; @@ -162,59 +127,13 @@ qreal ChatLog::layout(int start, int end, qreal width) start = clamp(start, 0, lines.size()); end = clamp(end + 1, 0, lines.size()); - qreal deltaY = 0.0; for(int i = start; i < end; ++i) { ChatLine* l = lines[i].get(); - qreal oldHeight = l->boundingSceneRect().height(); l->layout(width, QPointF(0.0, h)); - - if(oldHeight != l->boundingSceneRect().height()) - deltaY += oldHeight - l->boundingSceneRect().height(); - h += l->boundingSceneRect().height() + lineSpacing; } - - return deltaY; -} - -void ChatLog::updateVisibleLines() -{ - checkVisibility(); - - if(visibleLines.empty()) - return; - - bool stb = stickToBottom(); - - auto oldUpdateMode = viewportUpdateMode(); - setViewportUpdateMode(NoViewportUpdate); - - // Resize all lines currently visible in the viewport. - // If this creates some whitespace underneath the last visible lines - // then move the following non visible lines up in order to fill the gap. - qreal repos; - do - { - repos = 0; - if(!visibleLines.empty()) - { - repos = layout(visibleLines.first()->getRow(), visibleLines.last()->getRow(), useableWidth()); - reposition(visibleLines.last()->getRow()+1, visibleLines.last()->getRow()+10, -repos); - verticalScrollBar()->setValue(verticalScrollBar()->value() - repos); - } - - checkVisibility(); - } - while(repos != 0); - - checkVisibility(); - - setViewportUpdateMode(oldUpdateMode); - - if(stb) - scrollToBottom(); } void ChatLog::mousePressEvent(QMouseEvent* ev) @@ -443,14 +362,12 @@ void ChatLog::insertChatlineOnTop(const QList& newLines) if(newLines.isEmpty()) return; - bool stickToBtm = stickToBottom(); - - //move all lines down by n + // move all lines down by n int n = newLines.size(); for(ChatLine::Ptr l : lines) l->setRow(l->getRow() + n); - //add the new line + // add the new line for(ChatLine::Ptr l : newLines) { l->addToScene(scene); @@ -458,15 +375,8 @@ void ChatLog::insertChatlineOnTop(const QList& newLines) lines.prepend(l); } - //full refresh is required - layout(0, lines.size(), useableWidth()); - updateSceneRect(); - - if(stickToBtm) - scrollToBottom(); - - checkVisibility(); - updateTypingNotification(); + // redo layout + startResizeWorker(); } bool ChatLog::stickToBottom() const @@ -480,6 +390,27 @@ void ChatLog::scrollToBottom() verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } +void ChatLog::startResizeWorker() +{ + // (re)start the worker + if(!workerTimer->isActive()) + { + // these values must not be reevaluated while the worker is running + workerStb = stickToBottom(); + + if(!visibleLines.empty()) + workerAnchorLine = visibleLines.first(); + } + + workerLastIndex = 0; + workerTimer->start(); + + // switch to busy scene displaying the busy notification + setScene(busyScene); + updateBusyNotification(); + verticalScrollBar()->hide(); +} + void ChatLog::mouseDoubleClickEvent(QMouseEvent *ev) { QPointF scenePos = mapToScene(ev->pos()); @@ -580,6 +511,16 @@ void ChatLog::copySelectedText() const clipboard->setText(text); } +void ChatLog::setBusyNotification(ChatLine::Ptr notification) +{ + if(!notification.get()) + return; + + busyNotification = notification; + busyNotification->addToScene(busyScene); + busyNotification->visibilityChanged(true); +} + void ChatLog::setTypingNotification(ChatLine::Ptr notification) { typingNotification = notification; @@ -591,13 +532,22 @@ void ChatLog::setTypingNotification(ChatLine::Ptr notification) void ChatLog::setTypingNotificationVisible(bool visible) { - if(typingNotification.get() != nullptr) + if(typingNotification.get()) { typingNotification->setVisible(visible); updateTypingNotification(); } } +void ChatLog::scrollToLine(ChatLine::Ptr line) +{ + if(!line.get()) + return; + + updateSceneRect(); + verticalScrollBar()->setValue(line->boundingSceneRect().top()); +} + void ChatLog::checkVisibility() { if(lines.empty()) @@ -637,28 +587,13 @@ void ChatLog::checkVisibility() void ChatLog::scrollContentsBy(int dx, int dy) { QGraphicsView::scrollContentsBy(dx, dy); - updateVisibleLines(); + checkVisibility(); } void ChatLog::resizeEvent(QResizeEvent* ev) { - if(!workerTimer->isActive()) - { - workerStb = stickToBottom(); - workerLastIndex = 0; - workerDy = 0.0; - workerTimer->start(); - } - - bool stb = stickToBottom(); - + startResizeWorker(); QGraphicsView::resizeEvent(ev); - updateVisibleLines(); - updateMultiSelectionRect(); - updateTypingNotification(); - - if(stb) - scrollToBottom(); } void ChatLog::updateMultiSelectionRect() @@ -692,6 +627,15 @@ void ChatLog::updateTypingNotification() notification->layout(useableWidth(), QPointF(0.0, posY)); } +void ChatLog::updateBusyNotification() +{ + if(busyNotification.get()) + { + //repoisition the busy notification (centered) + busyNotification->layout(useableWidth(), getVisibleRect().topLeft() + QPointF(0, getVisibleRect().height()/2.0)); + } +} + ChatLine::Ptr ChatLog::findLineByPosY(qreal yPos) const { auto itr = std::lower_bound(lines.cbegin(), lines.cend(), yPos, ChatLine::lessThanBSRectBottom); @@ -728,3 +672,40 @@ void ChatLog::onSelectionTimerTimeout() break; } } + +void ChatLog::onWorkerTimeout() +{ + // Fairly arbitrary but + // large values will make the UI unresponsive + const int stepSize = 400; + + layout(workerLastIndex, workerLastIndex+stepSize, useableWidth()); + workerLastIndex += stepSize; + + // done? + if(workerLastIndex >= lines.size()) + { + workerTimer->stop(); + + // switch back to the scene containing the chat messages + setScene(scene); + + // make sure everything gets updated + updateSceneRect(); + checkVisibility(); + updateTypingNotification(); + updateMultiSelectionRect(); + + // scroll + if(workerStb) + scrollToBottom(); + else + scrollToLine(workerAnchorLine); + + // don't keep a Ptr to the anchor line + workerAnchorLine = ChatLine::Ptr(); + + // hidden during busy screen + verticalScrollBar()->show(); + } +} diff --git a/src/chatlog/chatlog.h b/src/chatlog/chatlog.h index ff7c67787..def258450 100644 --- a/src/chatlog/chatlog.h +++ b/src/chatlog/chatlog.h @@ -43,8 +43,10 @@ public: void clearSelection(); void clear(); void copySelectedText() const; + void setBusyNotification(ChatLine::Ptr notification); void setTypingNotification(ChatLine::Ptr notification); void setTypingNotificationVisible(bool visible); + void scrollToLine(ChatLine::Ptr line); QString getSelectedText() const; QString toPlainText() const; @@ -58,7 +60,7 @@ protected: QRect getVisibleRect() const; ChatLineContent* getContentFromPos(QPointF scenePos) const; - qreal layout(int start, int end, qreal width); + void layout(int start, int end, qreal width); bool isOverSelection(QPointF scenePos) const; bool stickToBottom() const; @@ -66,9 +68,9 @@ protected: void reposition(int start, int end, qreal deltaY); void updateSceneRect(); - void updateVisibleLines(); void checkVisibility(); void scrollToBottom(); + void startResizeWorker(); virtual void mouseDoubleClickEvent(QMouseEvent* ev); virtual void mousePressEvent(QMouseEvent* ev); @@ -79,11 +81,13 @@ protected: void updateMultiSelectionRect(); void updateTypingNotification(); + void updateBusyNotification(); ChatLine::Ptr findLineByPosY(qreal yPos) const; private slots: void onSelectionTimerTimeout(); + void onWorkerTimeout(); private: enum SelectionMode { @@ -99,9 +103,11 @@ private: }; QGraphicsScene* scene = nullptr; + QGraphicsScene* busyScene = nullptr; QVector lines; QList visibleLines; ChatLine::Ptr typingNotification; + ChatLine::Ptr busyNotification; // selection int selClickedRow = -1; //These 4 are only valid while selectionMode != None @@ -117,8 +123,8 @@ private: //worker vars int workerLastIndex = 0; - qreal workerDy = 0; bool workerStb = false; + ChatLine::Ptr workerAnchorLine; // actions QAction* copyAction = nullptr; diff --git a/src/chatlog/chatmessage.cpp b/src/chatlog/chatmessage.cpp index 768b491d5..c9d97dedd 100644 --- a/src/chatlog/chatmessage.cpp +++ b/src/chatlog/chatmessage.cpp @@ -101,8 +101,19 @@ ChatMessage::Ptr ChatMessage::createTypingNotification() { ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); + // Note: "[user]..." is just a placeholder. The actual text is set in ChatForm::setFriendTyping() msg->addColumn(new NotificationIcon(QSizeF(18, 18)), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); - msg->addColumn(new Text("%1 ...", Style::getFont(Style::Big), false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left)); + msg->addColumn(new Text("[user]...", Style::getFont(Style::Big), false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Left)); + + return msg; +} + +ChatMessage::Ptr ChatMessage::createBusyNotification() +{ + ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); + + // TODO: Bigger font + msg->addColumn(new Text(QObject::tr("Busy..."), Style::getFont(Style::ExtraBig), false, ""), ColumnFormat(1.0, ColumnFormat::VariableSize, ColumnFormat::Center)); return msg; } diff --git a/src/chatlog/chatmessage.h b/src/chatlog/chatmessage.h index 2fba9e68b..0cab27107 100644 --- a/src/chatlog/chatmessage.h +++ b/src/chatlog/chatmessage.h @@ -41,6 +41,7 @@ public: static ChatMessage::Ptr createChatInfoMessage(const QString& rawMessage, SystemMessageType type, const QDateTime& date); static ChatMessage::Ptr createFileTransferMessage(const QString& sender, ToxFile file, bool isMe, const QDateTime& date); static ChatMessage::Ptr createTypingNotification(); + static ChatMessage::Ptr createBusyNotification(); void markAsSent(const QDateTime& time); QString toString() const; diff --git a/src/widget/form/genericchatform.cpp b/src/widget/form/genericchatform.cpp index 77fd6171a..d63b40eaa 100644 --- a/src/widget/form/genericchatform.cpp +++ b/src/widget/form/genericchatform.cpp @@ -56,6 +56,7 @@ GenericChatForm::GenericChatForm(QWidget *parent) headTextLayout = new QVBoxLayout(); chatWidget = new ChatLog(this); + chatWidget->setBusyNotification(ChatMessage::createBusyNotification()); msgEdit = new ChatTextEdit();