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:
parent
9cb7ba06ed
commit
6e05782fb7
@ -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<int>(start, 0, lines.size());
|
||||
end = clamp<int>(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<ChatLine::Ptr>& 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<ChatLine::Ptr>& 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<ChatLine::Ptr> lines;
|
||||
QList<ChatLine::Ptr> 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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -56,6 +56,7 @@ GenericChatForm::GenericChatForm(QWidget *parent)
|
||||
headTextLayout = new QVBoxLayout();
|
||||
|
||||
chatWidget = new ChatLog(this);
|
||||
chatWidget->setBusyNotification(ChatMessage::createBusyNotification());
|
||||
|
||||
msgEdit = new ChatTextEdit();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user