1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

ChatLog: busy-screen during resize

Gets rid of most ugly hacks
This commit is contained in:
krepa098 2015-01-25 14:06:06 +01:00
parent 9cb7ba06ed
commit 6e05782fb7
5 changed files with 120 additions and 120 deletions

View File

@ -40,6 +40,7 @@ ChatLog::ChatLog(QWidget* parent)
: QGraphicsView(parent) : QGraphicsView(parent)
{ {
// Create the scene // Create the scene
busyScene = new QGraphicsScene(this);
scene = new QGraphicsScene(this); scene = new QGraphicsScene(this);
scene->setItemIndexMethod(QGraphicsScene::NoIndex); //Bsp-Tree is actually slower in this case scene->setItemIndexMethod(QGraphicsScene::NoIndex); //Bsp-Tree is actually slower in this case
setScene(scene); setScene(scene);
@ -80,43 +81,7 @@ ChatLog::ChatLog(QWidget* parent)
workerTimer = new QTimer(this); workerTimer = new QTimer(this);
workerTimer->setSingleShot(false); workerTimer->setSingleShot(false);
workerTimer->setInterval(100); workerTimer->setInterval(100);
connect(workerTimer, &QTimer::timeout, this, [this] { connect(workerTimer, &QTimer::timeout, this, &ChatLog::onWorkerTimeout);
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();
}
});
} }
void ChatLog::clearSelection() void ChatLog::clearSelection()
@ -147,10 +112,10 @@ void ChatLog::updateSceneRect()
setSceneRect(calculateSceneRect()); setSceneRect(calculateSceneRect());
} }
qreal ChatLog::layout(int start, int end, qreal width) void ChatLog::layout(int start, int end, qreal width)
{ {
if(lines.empty()) if(lines.empty())
return false; return;
qreal h = 0.0; qreal h = 0.0;
@ -162,59 +127,13 @@ qreal ChatLog::layout(int start, int end, qreal width)
start = clamp<int>(start, 0, lines.size()); start = clamp<int>(start, 0, lines.size());
end = clamp<int>(end + 1, 0, lines.size()); end = clamp<int>(end + 1, 0, lines.size());
qreal deltaY = 0.0;
for(int i = start; i < end; ++i) for(int i = start; i < end; ++i)
{ {
ChatLine* l = lines[i].get(); ChatLine* l = lines[i].get();
qreal oldHeight = l->boundingSceneRect().height();
l->layout(width, QPointF(0.0, h)); l->layout(width, QPointF(0.0, h));
if(oldHeight != l->boundingSceneRect().height())
deltaY += oldHeight - l->boundingSceneRect().height();
h += l->boundingSceneRect().height() + lineSpacing; 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) void ChatLog::mousePressEvent(QMouseEvent* ev)
@ -443,14 +362,12 @@ void ChatLog::insertChatlineOnTop(const QList<ChatLine::Ptr>& newLines)
if(newLines.isEmpty()) if(newLines.isEmpty())
return; return;
bool stickToBtm = stickToBottom(); // move all lines down by n
//move all lines down by n
int n = newLines.size(); int n = newLines.size();
for(ChatLine::Ptr l : lines) for(ChatLine::Ptr l : lines)
l->setRow(l->getRow() + n); l->setRow(l->getRow() + n);
//add the new line // add the new line
for(ChatLine::Ptr l : newLines) for(ChatLine::Ptr l : newLines)
{ {
l->addToScene(scene); l->addToScene(scene);
@ -458,15 +375,8 @@ void ChatLog::insertChatlineOnTop(const QList<ChatLine::Ptr>& newLines)
lines.prepend(l); lines.prepend(l);
} }
//full refresh is required // redo layout
layout(0, lines.size(), useableWidth()); startResizeWorker();
updateSceneRect();
if(stickToBtm)
scrollToBottom();
checkVisibility();
updateTypingNotification();
} }
bool ChatLog::stickToBottom() const bool ChatLog::stickToBottom() const
@ -480,6 +390,27 @@ void ChatLog::scrollToBottom()
verticalScrollBar()->setValue(verticalScrollBar()->maximum()); 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) void ChatLog::mouseDoubleClickEvent(QMouseEvent *ev)
{ {
QPointF scenePos = mapToScene(ev->pos()); QPointF scenePos = mapToScene(ev->pos());
@ -580,6 +511,16 @@ void ChatLog::copySelectedText() const
clipboard->setText(text); 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) void ChatLog::setTypingNotification(ChatLine::Ptr notification)
{ {
typingNotification = notification; typingNotification = notification;
@ -591,13 +532,22 @@ void ChatLog::setTypingNotification(ChatLine::Ptr notification)
void ChatLog::setTypingNotificationVisible(bool visible) void ChatLog::setTypingNotificationVisible(bool visible)
{ {
if(typingNotification.get() != nullptr) if(typingNotification.get())
{ {
typingNotification->setVisible(visible); typingNotification->setVisible(visible);
updateTypingNotification(); updateTypingNotification();
} }
} }
void ChatLog::scrollToLine(ChatLine::Ptr line)
{
if(!line.get())
return;
updateSceneRect();
verticalScrollBar()->setValue(line->boundingSceneRect().top());
}
void ChatLog::checkVisibility() void ChatLog::checkVisibility()
{ {
if(lines.empty()) if(lines.empty())
@ -637,28 +587,13 @@ void ChatLog::checkVisibility()
void ChatLog::scrollContentsBy(int dx, int dy) void ChatLog::scrollContentsBy(int dx, int dy)
{ {
QGraphicsView::scrollContentsBy(dx, dy); QGraphicsView::scrollContentsBy(dx, dy);
updateVisibleLines(); checkVisibility();
} }
void ChatLog::resizeEvent(QResizeEvent* ev) void ChatLog::resizeEvent(QResizeEvent* ev)
{ {
if(!workerTimer->isActive()) startResizeWorker();
{
workerStb = stickToBottom();
workerLastIndex = 0;
workerDy = 0.0;
workerTimer->start();
}
bool stb = stickToBottom();
QGraphicsView::resizeEvent(ev); QGraphicsView::resizeEvent(ev);
updateVisibleLines();
updateMultiSelectionRect();
updateTypingNotification();
if(stb)
scrollToBottom();
} }
void ChatLog::updateMultiSelectionRect() void ChatLog::updateMultiSelectionRect()
@ -692,6 +627,15 @@ void ChatLog::updateTypingNotification()
notification->layout(useableWidth(), QPointF(0.0, posY)); 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 ChatLine::Ptr ChatLog::findLineByPosY(qreal yPos) const
{ {
auto itr = std::lower_bound(lines.cbegin(), lines.cend(), yPos, ChatLine::lessThanBSRectBottom); auto itr = std::lower_bound(lines.cbegin(), lines.cend(), yPos, ChatLine::lessThanBSRectBottom);
@ -728,3 +672,40 @@ void ChatLog::onSelectionTimerTimeout()
break; 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();
}
}

View File

@ -43,8 +43,10 @@ public:
void clearSelection(); void clearSelection();
void clear(); void clear();
void copySelectedText() const; void copySelectedText() const;
void setBusyNotification(ChatLine::Ptr notification);
void setTypingNotification(ChatLine::Ptr notification); void setTypingNotification(ChatLine::Ptr notification);
void setTypingNotificationVisible(bool visible); void setTypingNotificationVisible(bool visible);
void scrollToLine(ChatLine::Ptr line);
QString getSelectedText() const; QString getSelectedText() const;
QString toPlainText() const; QString toPlainText() const;
@ -58,7 +60,7 @@ protected:
QRect getVisibleRect() const; QRect getVisibleRect() const;
ChatLineContent* getContentFromPos(QPointF scenePos) 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 isOverSelection(QPointF scenePos) const;
bool stickToBottom() const; bool stickToBottom() const;
@ -66,9 +68,9 @@ protected:
void reposition(int start, int end, qreal deltaY); void reposition(int start, int end, qreal deltaY);
void updateSceneRect(); void updateSceneRect();
void updateVisibleLines();
void checkVisibility(); void checkVisibility();
void scrollToBottom(); void scrollToBottom();
void startResizeWorker();
virtual void mouseDoubleClickEvent(QMouseEvent* ev); virtual void mouseDoubleClickEvent(QMouseEvent* ev);
virtual void mousePressEvent(QMouseEvent* ev); virtual void mousePressEvent(QMouseEvent* ev);
@ -79,11 +81,13 @@ protected:
void updateMultiSelectionRect(); void updateMultiSelectionRect();
void updateTypingNotification(); void updateTypingNotification();
void updateBusyNotification();
ChatLine::Ptr findLineByPosY(qreal yPos) const; ChatLine::Ptr findLineByPosY(qreal yPos) const;
private slots: private slots:
void onSelectionTimerTimeout(); void onSelectionTimerTimeout();
void onWorkerTimeout();
private: private:
enum SelectionMode { enum SelectionMode {
@ -99,9 +103,11 @@ private:
}; };
QGraphicsScene* scene = nullptr; QGraphicsScene* scene = nullptr;
QGraphicsScene* busyScene = nullptr;
QVector<ChatLine::Ptr> lines; QVector<ChatLine::Ptr> lines;
QList<ChatLine::Ptr> visibleLines; QList<ChatLine::Ptr> visibleLines;
ChatLine::Ptr typingNotification; ChatLine::Ptr typingNotification;
ChatLine::Ptr busyNotification;
// selection // selection
int selClickedRow = -1; //These 4 are only valid while selectionMode != None int selClickedRow = -1; //These 4 are only valid while selectionMode != None
@ -117,8 +123,8 @@ private:
//worker vars //worker vars
int workerLastIndex = 0; int workerLastIndex = 0;
qreal workerDy = 0;
bool workerStb = false; bool workerStb = false;
ChatLine::Ptr workerAnchorLine;
// actions // actions
QAction* copyAction = nullptr; QAction* copyAction = nullptr;

View File

@ -101,8 +101,19 @@ ChatMessage::Ptr ChatMessage::createTypingNotification()
{ {
ChatMessage::Ptr msg = ChatMessage::Ptr(new ChatMessage); 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 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; return msg;
} }

View File

@ -41,6 +41,7 @@ public:
static ChatMessage::Ptr createChatInfoMessage(const QString& rawMessage, SystemMessageType type, const QDateTime& date); 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 createFileTransferMessage(const QString& sender, ToxFile file, bool isMe, const QDateTime& date);
static ChatMessage::Ptr createTypingNotification(); static ChatMessage::Ptr createTypingNotification();
static ChatMessage::Ptr createBusyNotification();
void markAsSent(const QDateTime& time); void markAsSent(const QDateTime& time);
QString toString() const; QString toString() const;

View File

@ -56,6 +56,7 @@ GenericChatForm::GenericChatForm(QWidget *parent)
headTextLayout = new QVBoxLayout(); headTextLayout = new QVBoxLayout();
chatWidget = new ChatLog(this); chatWidget = new ChatLog(this);
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
msgEdit = new ChatTextEdit(); msgEdit = new ChatTextEdit();