[app] Delay repaint after color scheme change

That allows settings page animation to complete smoothly.

Also switched BooksPageWidget from QTimer to QBasicTimer for better
efficiency.
This commit is contained in:
Slava Monich 2022-10-11 02:47:25 +03:00
parent a4a868c1c8
commit ba04787927
2 changed files with 90 additions and 40 deletions

View file

@ -48,6 +48,7 @@
#include <QPainter> #include <QPainter>
static const QString IMAGE_URL("image://%1/%2"); static const QString IMAGE_URL("image://%1/%2");
static const int REPAINT_DELAY_MSEC = 250;
// ========================================================================== // ==========================================================================
// BooksPageWidget::Data // BooksPageWidget::Data
@ -533,7 +534,6 @@ BooksPageWidget::BooksPageWidget(QQuickItem* aParent) :
iSettings(BooksSettings::sharedInstance()), iSettings(BooksSettings::sharedInstance()),
iTaskQueue(BooksTaskQueue::defaultQueue()), iTaskQueue(BooksTaskQueue::defaultQueue()),
iTextStyle(BooksTextStyle::defaults()), iTextStyle(BooksTextStyle::defaults()),
iResizeTimer(new QTimer(this)),
iModel(NULL), iModel(NULL),
iResetTask(NULL), iResetTask(NULL),
iRenderTask(NULL), iRenderTask(NULL),
@ -551,9 +551,6 @@ BooksPageWidget::BooksPageWidget(QQuickItem* aParent) :
{ {
connect(iSettings.data(), SIGNAL(colorSchemeChanged()), SLOT(onColorsChanged())); connect(iSettings.data(), SIGNAL(colorSchemeChanged()), SLOT(onColorsChanged()));
setFlag(ItemHasContents, true); setFlag(ItemHasContents, true);
iResizeTimer->setSingleShot(true);
iResizeTimer->setInterval(0);
connect(iResizeTimer, SIGNAL(timeout()), SLOT(onResizeTimeout()));
connect(this, SIGNAL(widthChanged()), SLOT(onWidthChanged())); connect(this, SIGNAL(widthChanged()), SLOT(onWidthChanged()));
connect(this, SIGNAL(heightChanged()), SLOT(onHeightChanged())); connect(this, SIGNAL(heightChanged()), SLOT(onHeightChanged()));
} }
@ -650,7 +647,7 @@ BooksPageWidget::onColorsChanged()
{ {
HDEBUG(iPage); HDEBUG(iPage);
HASSERT(sender() == iSettings); HASSERT(sender() == iSettings);
scheduleRepaint(); scheduleRepaintDelayUpdate();
} }
void void
@ -797,7 +794,7 @@ BooksPageWidget::resetView()
} }
} }
if (!iEmpty) { if (!iEmpty) {
update(); updateNow();
} }
} }
@ -816,18 +813,40 @@ BooksPageWidget::scheduleRepaint()
{ {
BooksLoadingSignalBlocker block(this); BooksLoadingSignalBlocker block(this);
cancelRepaint(); cancelRepaint();
const int w = width(); if (width() > 0 && height() > 0) {
const int h = height(); if (!iData.isNull() && !iData->iView.isNull()) {
if (w > 0 && h > 0 && !iData.isNull() && !iData->iView.isNull()) { (iRenderTask = new RenderTask(iTaskQueue->pool(), thread(),
const shared_ptr<BooksTextView> view(iData->iView); iData, iSettings->colorScheme()))->submit(this,
(iRenderTask = new RenderTask(iTaskQueue->pool(), thread(), SLOT(onRenderTaskDone()));
iData, iSettings->colorScheme()))-> } else {
submit(this, SLOT(onRenderTaskDone())); updateNow();
} else { }
update();
} }
} }
void
BooksPageWidget::scheduleRepaintDelayUpdate()
{
BooksLoadingSignalBlocker block(this);
cancelRepaint();
if (width() > 0 && height() > 0) {
if (!iData.isNull() && !iData->iView.isNull()) {
(iRenderTask = new RenderTask(iTaskQueue->pool(), thread(),
iData, iSettings->colorScheme()))->submit(this,
SLOT(onRenderTaskDoneDelayUpdate()));
} else if (!iDelayUpdateTimer.isActive()) {
iDelayUpdateTimer.start(REPAINT_DELAY_MSEC, this);
}
}
}
void
BooksPageWidget::updateNow()
{
iDelayUpdateTimer.stop();
update();
}
void void
BooksPageWidget::onResetTaskDone() BooksPageWidget::onResetTaskDone()
{ {
@ -841,14 +860,47 @@ BooksPageWidget::onResetTaskDone()
} }
void void
BooksPageWidget::onRenderTaskDone() BooksPageWidget::renderTaskDone()
{ {
BooksLoadingSignalBlocker block(this);
HASSERT(sender() == iRenderTask); HASSERT(sender() == iRenderTask);
iImage = iRenderTask->iImage; iImage = iRenderTask->iImage;
iRenderTask->release(this); iRenderTask->release(this);
iRenderTask = NULL; iRenderTask = NULL;
update(); }
void
BooksPageWidget::onRenderTaskDone()
{
BooksLoadingSignalBlocker block(this);
renderTaskDone();
updateNow();
}
void
BooksPageWidget::onRenderTaskDoneDelayUpdate()
{
BooksLoadingSignalBlocker block(this);
renderTaskDone();
if (!iDelayUpdateTimer.isActive()) {
iDelayUpdateTimer.start(REPAINT_DELAY_MSEC, this);
}
}
void
BooksPageWidget::timerEvent(
QTimerEvent* aEvent)
{
const int timerId = aEvent->timerId();
if (timerId == iResizeTimer.timerId()) {
// This can only happen if only width or height has changed.
// Normally, width change is followed by height change and
// the size is updated from the setHeight() method
updateSize();
} else if (timerId == iDelayUpdateTimer.timerId()) {
updateNow();
} else {
return QQuickPaintedItem::timerEvent(aEvent);
}
} }
void void
@ -882,7 +934,7 @@ BooksPageWidget::onClearSelectionTaskDone()
if (task->iImageUpdated) { if (task->iImageUpdated) {
iImage = task->iImage; iImage = task->iImage;
update(); updateNow();
} }
task->release(this); task->release(this);
@ -913,7 +965,7 @@ BooksPageWidget::onStartSelectionTaskDone()
if (emitSelectionEmpty) { if (emitSelectionEmpty) {
Q_EMIT selectionEmptyChanged(); Q_EMIT selectionEmptyChanged();
} }
update(); updateNow();
} }
task->release(this); task->release(this);
@ -933,7 +985,7 @@ BooksPageWidget::onExtendSelectionTaskDone()
HDEBUG("selection" << iSelectionEmpty); HDEBUG("selection" << iSelectionEmpty);
Q_EMIT selectionEmptyChanged(); Q_EMIT selectionEmptyChanged();
} }
update(); updateNow();
} }
task->release(this); task->release(this);
@ -1042,6 +1094,7 @@ void
BooksPageWidget::updateSize() BooksPageWidget::updateSize()
{ {
HDEBUG("page" << iPage << QSize(width(), height())); HDEBUG("page" << iPage << QSize(width(), height()));
iResizeTimer.stop();
iImage = QImage(); iImage = QImage();
resetView(); resetView();
} }
@ -1051,35 +1104,25 @@ BooksPageWidget::onWidthChanged()
{ {
HVERBOSE((int)width()); HVERBOSE((int)width());
// Width change will probably be followed by height change // Width change will probably be followed by height change
iResizeTimer->start(); iResizeTimer.start(0, this);
iImage = QImage(); iImage = QImage();
update(); updateNow();
} }
void void
BooksPageWidget::onHeightChanged() BooksPageWidget::onHeightChanged()
{ {
HVERBOSE((int)height()); HVERBOSE((int)height());
if (iResizeTimer->isActive()) { if (iResizeTimer.isActive()) { // Started by onWidthChanged()
// Height is usually changed after width, repaint right away // Height is usually changed after width, repaint right away
iResizeTimer->stop();
updateSize(); updateSize();
} else { } else {
iResizeTimer->start(); iResizeTimer.start(0, this);
iImage = QImage(); iImage = QImage();
update(); updateNow();
} }
} }
void
BooksPageWidget::onResizeTimeout()
{
// This can only happen if only width or height has changed. Normally,
// width change is followed by height change and view is reset from the
// setHeight() method
updateSize();
}
void void
BooksPageWidget::handleLongPress( BooksPageWidget::handleLongPress(
int aX, int aX,

View file

@ -45,7 +45,7 @@
#include "ZLTextStyle.h" #include "ZLTextStyle.h"
#include <QQuickPaintedItem> #include <QQuickPaintedItem>
#include <QTimer> #include <QBasicTimer>
#include <QList> #include <QList>
class BooksPageWidget: public QQuickPaintedItem, private BooksLoadingProperty class BooksPageWidget: public QQuickPaintedItem, private BooksLoadingProperty
@ -129,12 +129,12 @@ Q_SIGNALS:
private Q_SLOTS: private Q_SLOTS:
void onWidthChanged(); void onWidthChanged();
void onHeightChanged(); void onHeightChanged();
void onResizeTimeout();
void onBookModelDestroyed(); void onBookModelDestroyed();
void onTextStyleChanged(); void onTextStyleChanged();
void onColorsChanged(); void onColorsChanged();
void onResetTaskDone(); void onResetTaskDone();
void onRenderTaskDone(); void onRenderTaskDone();
void onRenderTaskDoneDelayUpdate();
void onClearSelectionTaskDone(); void onClearSelectionTaskDone();
void onStartSelectionTaskDone(); void onStartSelectionTaskDone();
void onExtendSelectionTaskDone(); void onExtendSelectionTaskDone();
@ -142,13 +142,19 @@ private Q_SLOTS:
void onLongPressTaskDone(); void onLongPressTaskDone();
void onFootnoteTaskDone(); void onFootnoteTaskDone();
private: protected:
void timerEvent(QTimerEvent*) Q_DECL_OVERRIDE;
void paint(QPainter*) Q_DECL_OVERRIDE; void paint(QPainter*) Q_DECL_OVERRIDE;
private:
void updateSize(); void updateSize();
void resetView(); void resetView();
void releaseExtendSelectionTasks(); void releaseExtendSelectionTasks();
void scheduleRepaint(); void scheduleRepaint();
void scheduleRepaintDelayUpdate();
void cancelRepaint(); void cancelRepaint();
void renderTaskDone();
void updateNow();
private: private:
class ResetTask; class ResetTask;
@ -163,7 +169,8 @@ private:
shared_ptr<BooksTaskQueue> iTaskQueue; shared_ptr<BooksTaskQueue> iTaskQueue;
shared_ptr<ZLTextStyle> iTextStyle; shared_ptr<ZLTextStyle> iTextStyle;
BooksPos iBookPos; BooksPos iBookPos;
QTimer* iResizeTimer; QBasicTimer iResizeTimer;
QBasicTimer iDelayUpdateTimer;
BooksBookModel* iModel; BooksBookModel* iModel;
BooksMargins iMargins; BooksMargins iMargins;
shared_ptr<Data> iData; shared_ptr<Data> iData;