Asynchronous calculation of document text layout

This commit is contained in:
Jakub Melka 2020-01-01 18:23:18 +01:00
parent c832c4ecef
commit e9481fc446
12 changed files with 313 additions and 81 deletions

View File

@ -18,6 +18,7 @@
#include "pdfcompiler.h"
#include "pdfcms.h"
#include "pdfdrawspacecontroller.h"
#include "pdfprogress.h"
#include <QtConcurrent/QtConcurrent>
@ -74,7 +75,6 @@ void PDFAsynchronousPageCompiler::stop()
}
m_tasks.clear();
m_cache.clear();
m_textLayouts.dirty();
m_state = State::Inactive;
break;
@ -121,7 +121,7 @@ const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFIntege
return compiledPage;
};
m_proxy->getFontCache()->setCacheShrinkEnabled(false);
m_proxy->getFontCache()->setCacheShrinkEnabled(this, false);
CompileTask& task = m_tasks[pageIndex];
task.taskFuture = QtConcurrent::run(compilePage);
task.taskWatcher = new QFutureWatcher<PDFPrecompiledPage>(this);
@ -132,17 +132,6 @@ const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFIntege
return page;
}
PDFTextLayout PDFAsynchronousPageCompiler::getTextLayout(PDFInteger pageIndex)
{
if (m_state != State::Active || !m_proxy->getDocument())
{
// Engine is not active, always return empty layout
return PDFTextLayout();
}
return m_textLayouts.get(this, &PDFAsynchronousPageCompiler::getTextLayoutsImpl).getTextLayout(pageIndex);
}
void PDFAsynchronousPageCompiler::onPageCompiled()
{
std::vector<PDFInteger> compiledPages;
@ -182,7 +171,7 @@ void PDFAsynchronousPageCompiler::onPageCompiled()
}
// We allow font cache shrinking, when we aren't doing something in parallel.
m_proxy->getFontCache()->setCacheShrinkEnabled(m_tasks.empty());
m_proxy->getFontCache()->setCacheShrinkEnabled(this, m_tasks.empty());
if (!compiledPages.empty())
{
@ -268,38 +257,152 @@ void PDFTextLayoutGenerator::performOutputCharacter(const PDFTextCharacterInfo&
m_textLayout.addCharacter(info);
}
PDFTextLayoutStorage PDFAsynchronousPageCompiler::getTextLayoutsImpl()
PDFAsynchronousTextLayoutCompiler::PDFAsynchronousTextLayoutCompiler(PDFDrawWidgetProxy* proxy) :
BaseClass(proxy),
m_proxy(proxy)
{
m_proxy->getFontCache()->setCacheShrinkEnabled(false);
const PDFCatalog* catalog = m_proxy->getDocument()->getCatalog();
connect(&m_textLayoutCompileFutureWatcher, &QFutureWatcher<PDFTextLayoutStorage>::finished, this, &PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated);
}
PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
PDFTextLayoutStorage result(catalog->getPageCount());
QMutex mutex;
auto generateTextLayout = [this, &result, &mutex, &cms, catalog](PDFInteger pageIndex)
void PDFAsynchronousTextLayoutCompiler::start()
{
switch (m_state)
{
if (!catalog->getPage(pageIndex))
case State::Inactive:
{
// Invalid page index
result.setTextLayout(pageIndex, PDFTextLayout(), &mutex);
return;
m_state = State::Active;
break;
}
const PDFPage* page = catalog->getPage(pageIndex);
Q_ASSERT(page);
case State::Active:
break; // We have nothing to do...
PDFTextLayoutGenerator generator(m_proxy->getFeatures(), page, m_proxy->getDocument(), m_proxy->getFontCache(), cms.data(), m_proxy->getOptionalContentActivity(), QMatrix(), m_proxy->getMeshQualitySettings());
generator.processContents();
result.setTextLayout(pageIndex, generator.createTextLayout(), &mutex);
case State::Stopping:
{
// We shouldn't call this function while stopping!
Q_ASSERT(false);
break;
}
}
}
void PDFAsynchronousTextLayoutCompiler::stop()
{
switch (m_state)
{
case State::Inactive:
break; // We have nothing to do...
case State::Active:
{
// Stop the engine
m_state = State::Stopping;
m_textLayoutCompileFutureWatcher.waitForFinished();
m_textLayouts = std::nullopt;
m_state = State::Inactive;
break;
}
case State::Stopping:
{
// We shouldn't call this function while stopping!
Q_ASSERT(false);
break;
}
}
}
void PDFAsynchronousTextLayoutCompiler::reset()
{
stop();
start();
}
PDFTextLayout PDFAsynchronousTextLayoutCompiler::getTextLayout(PDFInteger pageIndex)
{
if (m_state != State::Active || !m_proxy->getDocument())
{
// Engine is not active, always return empty layout
return PDFTextLayout();
}
if (m_textLayouts)
{
return m_textLayouts->getTextLayout(pageIndex);
}
return PDFTextLayout();
}
void PDFAsynchronousTextLayoutCompiler::makeTextLayout()
{
if (m_state != State::Active || !m_proxy->getDocument())
{
// Engine is not active, do not calculate layout
return;
}
if (m_textLayouts.has_value())
{
// Value is computed already
return;
}
if (m_textLayoutCompileFuture.isRunning())
{
// Text layout is already being processed
return;
}
ProgressStartupInfo info;
info.showDialog = true;
info.text = tr("Generating text layouts for pages...");
m_proxy->getFontCache()->setCacheShrinkEnabled(this, false);
const PDFCatalog* catalog = m_proxy->getDocument()->getCatalog();
m_proxy->getProgress()->start(catalog->getPageCount(), qMove(info));
PDFCMSPointer cms = m_proxy->getCMSManager()->getCurrentCMS();
auto createTextLayout = [this, cms, catalog]() -> PDFTextLayoutStorage
{
PDFTextLayoutStorage result(catalog->getPageCount());
QMutex mutex;
auto generateTextLayout = [this, &result, &mutex, cms, catalog](PDFInteger pageIndex)
{
if (!catalog->getPage(pageIndex))
{
// Invalid page index
result.setTextLayout(pageIndex, PDFTextLayout(), &mutex);
return;
}
const PDFPage* page = catalog->getPage(pageIndex);
Q_ASSERT(page);
PDFTextLayoutGenerator generator(m_proxy->getFeatures(), page, m_proxy->getDocument(), m_proxy->getFontCache(), cms.data(), m_proxy->getOptionalContentActivity(), QMatrix(), m_proxy->getMeshQualitySettings());
generator.processContents();
result.setTextLayout(pageIndex, generator.createTextLayout(), &mutex);
m_proxy->getProgress()->step();
};
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
return result;
};
auto pageRange = PDFIntegerRange<PDFInteger>(0, catalog->getPageCount());
std::for_each(std::execution::parallel_policy(), pageRange.begin(), pageRange.end(), generateTextLayout);
Q_ASSERT(!m_textLayoutCompileFuture.isRunning());
m_textLayoutCompileFuture = QtConcurrent::run(createTextLayout);
m_textLayoutCompileFutureWatcher.setFuture(m_textLayoutCompileFuture);
}
// We allow font cache shrinking, when we aren't doing something in parallel.
m_proxy->getFontCache()->setCacheShrinkEnabled(m_tasks.empty());
void PDFAsynchronousTextLayoutCompiler::onTextLayoutCreated()
{
m_proxy->getFontCache()->setCacheShrinkEnabled(this, true);
m_proxy->getProgress()->finish();
return result;
m_textLayouts = m_textLayoutCompileFuture.result();
emit textLayoutChanged();
}
} // namespace pdf

View File

@ -73,11 +73,6 @@ public:
/// \param compile Compile the page, if it is not found in the cache
const PDFPrecompiledPage* getCompiledPage(PDFInteger pageIndex, bool compile);
/// Returns text layout of the page. If page index is invalid,
/// then empty text layout is returned.
/// \param pageIndex Page index
PDFTextLayout getTextLayout(PDFInteger pageIndex);
signals:
void pageImageChanged(bool all, const std::vector<PDFInteger>& pages);
void renderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors);
@ -85,9 +80,6 @@ signals:
private:
void onPageCompiled();
/// Returns text layouts for all pages
PDFTextLayoutStorage getTextLayoutsImpl();
struct CompileTask
{
QFuture<PDFPrecompiledPage> taskFuture;
@ -98,7 +90,57 @@ private:
State m_state = State::Inactive;
QCache<PDFInteger, PDFPrecompiledPage> m_cache;
std::map<PDFInteger, CompileTask> m_tasks;
PDFCachedItem<PDFTextLayoutStorage> m_textLayouts;
};
class PDFAsynchronousTextLayoutCompiler : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFAsynchronousTextLayoutCompiler(PDFDrawWidgetProxy* proxy);
/// Starts the engine. Call this function only if the engine
/// is stopped.
void start();
/// Stops the engine and all underlying asynchronous tasks. Also
/// clears the cache. Call this function only if engine is active.
void stop();
/// Resets the engine - calls stop and then calls start.
void reset();
enum class State
{
Inactive,
Active,
Stopping
};
/// Returns text layout of the page. If page index is invalid,
/// then empty text layout is returned.
/// \param pageIndex Page index
PDFTextLayout getTextLayout(PDFInteger pageIndex);
/// Create text layout for the document. Function is asynchronous,
/// it returns immediately. After text layout is created, signal
/// \p textLayoutChanged is emitted.
void makeTextLayout();
signals:
void textLayoutChanged();
private:
void onTextLayoutCreated();
PDFDrawWidgetProxy* m_proxy;
State m_state = State::Inactive;
std::optional<PDFTextLayoutStorage> m_textLayouts;
QFuture<PDFTextLayoutStorage> m_textLayoutCompileFuture;
QFutureWatcher<PDFTextLayoutStorage> m_textLayoutCompileFutureWatcher;
};
} // namespace pdf

View File

@ -269,7 +269,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
};
// Now, we are ready to scan all objects
progressStart(occupiedEntries.size());
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Reading contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), processEntry);
progressFinish();
@ -369,7 +369,7 @@ PDFDocument PDFDocumentReader::readFromBuffer(const QByteArray& buffer)
objects[entry.reference.objectNumber].object = securityHandler->decryptObject(objects[entry.reference.objectNumber].object, entry.reference);
};
progressStart(occupiedEntries.size());
progressStart(occupiedEntries.size(), PDFTranslationContext::tr("Decrypting encrypted contents of document..."));
std::for_each(std::execution::parallel_policy(), occupiedEntries.cbegin(), occupiedEntries.cend(), decryptEntry);
progressFinish();
}
@ -529,11 +529,15 @@ int PDFDocumentReader::findFromEnd(const char* what, const QByteArray& byteArray
return FIND_NOT_FOUND_RESULT;
}
void PDFDocumentReader::progressStart(size_t stepCount)
void PDFDocumentReader::progressStart(size_t stepCount, QString text)
{
if (m_progress)
{
m_progress->start(stepCount);
ProgressStartupInfo info;
info.showDialog = !text.isEmpty();
info.text = qMove(text);
m_progress->start(stepCount, qMove(info));
}
}

View File

@ -87,7 +87,7 @@ private:
/// \returns Position of string, or FIND_NOT_FOUND_RESULT
int findFromEnd(const char* what, const QByteArray& byteArray, int limit);
void progressStart(size_t stepCount);
void progressStart(size_t stepCount, QString text);
void progressStep();
void progressFinish();

View File

@ -59,6 +59,11 @@ void PDFDrawSpaceController::setDocument(const PDFDocument* document, const PDFO
}
}
void PDFDrawSpaceController::onOptionalContentGroupStateChanged()
{
emit pageImageChanged(true, { });
}
void PDFDrawSpaceController::setPageLayout(PageLayout pageLayout)
{
if (m_pageLayoutMode != pageLayout)
@ -138,11 +143,6 @@ QSizeF PDFDrawSpaceController::getReferenceBoundingBox() const
return rect.size();
}
void PDFDrawSpaceController::onOptionalContentGroupStateChanged()
{
emit pageImageChanged(true, { });
}
void PDFDrawSpaceController::recalculate()
{
if (!m_document)
@ -400,7 +400,9 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
m_verticalScrollbar(nullptr),
m_features(PDFRenderer::getDefaultFeatures()),
m_compiler(new PDFAsynchronousPageCompiler(this)),
m_rasterizer(new PDFRasterizer(this))
m_textLayoutCompiler(new PDFAsynchronousTextLayoutCompiler(this)),
m_rasterizer(new PDFRasterizer(this)),
m_progress(nullptr)
{
m_controller = new PDFDrawSpaceController(this);
connect(m_controller, &PDFDrawSpaceController::drawSpaceChanged, this, &PDFDrawWidgetProxy::update);
@ -408,6 +410,7 @@ PDFDrawWidgetProxy::PDFDrawWidgetProxy(QObject* parent) :
connect(m_controller, &PDFDrawSpaceController::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
connect(m_compiler, &PDFAsynchronousPageCompiler::renderingError, this, &PDFDrawWidgetProxy::renderingError);
connect(m_compiler, &PDFAsynchronousPageCompiler::pageImageChanged, this, &PDFDrawWidgetProxy::pageImageChanged);
connect(m_textLayoutCompiler, &PDFAsynchronousTextLayoutCompiler::textLayoutChanged, this, &PDFDrawWidgetProxy::onTextLayoutChanged);
}
PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
@ -418,8 +421,10 @@ PDFDrawWidgetProxy::~PDFDrawWidgetProxy()
void PDFDrawWidgetProxy::setDocument(const PDFDocument* document, const PDFOptionalContentActivity* optionalContentActivity)
{
m_compiler->stop();
m_textLayoutCompiler->stop();
m_controller->setDocument(document, optionalContentActivity);
m_compiler->start();
m_textLayoutCompiler->start();
}
void PDFDrawWidgetProxy::init(PDFWidget* widget)
@ -626,7 +631,8 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
// Draw text blocks/text lines, if it is enabled
if (m_features.testFlag(PDFRenderer::DebugTextBlocks))
{
PDFTextLayout layout = m_compiler->getTextLayout(item.pageIndex);
m_textLayoutCompiler->makeTextLayout();
PDFTextLayout layout = m_textLayoutCompiler->getTextLayout(item.pageIndex);
const PDFTextBlocks& textBlocks = layout.getTextBlocks();
painter->save();
@ -648,7 +654,8 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect)
}
if (m_features.testFlag(PDFRenderer::DebugTextLines))
{
PDFTextLayout layout = m_compiler->getTextLayout(item.pageIndex);
m_textLayoutCompiler->makeTextLayout();
PDFTextLayout layout = m_textLayoutCompiler->getTextLayout(item.pageIndex);
const PDFTextBlocks& textBlocks = layout.getTextBlocks();
painter->save();
@ -989,6 +996,11 @@ QRectF PDFDrawWidgetProxy::fromDeviceSpace(const QRectF& rect) const
rect.height() * m_deviceSpaceUnitToPixel);
}
void PDFDrawWidgetProxy::onTextLayoutChanged()
{
emit repaintNeeded();
}
bool PDFDrawWidgetProxy::isBlockMode() const
{
switch (m_controller->getPageLayout())
@ -1137,8 +1149,10 @@ void PDFDrawWidgetProxy::setFeatures(PDFRenderer::Features features)
if (m_features != features)
{
m_compiler->stop();
m_textLayoutCompiler->stop();
m_features = features;
m_compiler->start();
m_textLayoutCompiler->start();
emit pageImageChanged(true, { });
}
}

View File

@ -32,10 +32,12 @@ class QScrollBar;
namespace pdf
{
class PDFProgress;
class PDFWidget;
class IDrawWidget;
class PDFCMSManager;
class PDFAsynchronousPageCompiler;
class PDFAsynchronousTextLayoutCompiler;
/// This class controls draw space - page layout. Pages are divided into blocks
/// each block can contain one or multiple pages. Units are in milimeters.
@ -274,6 +276,8 @@ public:
const PDFMeshQualitySettings& getMeshQualitySettings() const { return m_meshQualitySettings; }
PDFAsynchronousPageCompiler* getCompiler() const { return m_compiler; }
const PDFCMSManager* getCMSManager() const;
PDFProgress* getProgress() const { return m_progress; }
void setProgress(PDFProgress* progress) { m_progress = progress; }
void setFeatures(PDFRenderer::Features features);
void setPreferredMeshResolutionRatio(PDFReal ratio);
@ -326,6 +330,7 @@ private:
/// Converts rectangle from device space to the pixel space
QRectF fromDeviceSpace(const QRectF& rect) const;
void onTextLayoutChanged();
void onColorManagementSystemChanged();
void onHorizontalScrollbarValueChanged(int value);
void onVerticalScrollbarValueChanged(int value);
@ -408,8 +413,14 @@ private:
/// Page compiler
PDFAsynchronousPageCompiler* m_compiler;
/// Text layout compiler
PDFAsynchronousTextLayoutCompiler* m_textLayoutCompiler;
/// Page image rasterizer for thumbnails
PDFRasterizer* m_rasterizer;
/// Progress
PDFProgress* m_progress;
};
} // namespace pdf

View File

@ -1610,7 +1610,7 @@ PDFFontPointer PDFFontCache::getFont(const PDFObject& fontObject) const
// We must create the font
PDFFontPointer font = PDFFont::createFont(fontObject, m_document);
if (m_fontCacheShrinkEnabled && m_fontCache.size() >= m_fontCacheLimit)
if (m_fontCacheShrinkDisabledObjects.empty() && m_fontCache.size() >= m_fontCacheLimit)
{
// We have exceeded the cache limit. Clear the cache.
m_fontCache.clear();
@ -1638,7 +1638,7 @@ PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font,
// We must create the realized font
PDFRealizedFontPointer realizedFont = PDFRealizedFont::createRealizedFont(font, size, reporter);
if (m_fontCacheShrinkEnabled && m_realizedFontCache.size() >= m_realizedFontCacheLimit)
if (m_fontCacheShrinkDisabledObjects.empty() && m_realizedFontCache.size() >= m_realizedFontCacheLimit)
{
m_realizedFontCache.clear();
}
@ -1649,13 +1649,19 @@ PDFRealizedFontPointer PDFFontCache::getRealizedFont(const PDFFontPointer& font,
return it->second;
}
void PDFFontCache::setCacheShrinkEnabled(bool enabled)
void PDFFontCache::setCacheShrinkEnabled(void* source, bool enabled)
{
if (m_fontCacheShrinkEnabled != enabled)
QMutexLocker lock(&m_mutex);
if (enabled)
{
m_fontCacheShrinkEnabled = enabled;
m_fontCacheShrinkDisabledObjects.erase(source);
lock.unlock();
shrink();
}
else
{
m_fontCacheShrinkDisabledObjects.insert(source);
}
}
void PDFFontCache::setCacheLimits(int fontCacheLimit, int instancedFontCacheLimit)
@ -1670,9 +1676,9 @@ void PDFFontCache::setCacheLimits(int fontCacheLimit, int instancedFontCacheLimi
void PDFFontCache::shrink()
{
if (m_fontCacheShrinkEnabled)
QMutexLocker lock(&m_mutex);
if (m_fontCacheShrinkDisabledObjects.empty())
{
QMutexLocker lock(&m_mutex);
if (m_fontCache.size() >= m_fontCacheLimit)
{
m_fontCache.clear();

View File

@ -27,6 +27,7 @@
#include <QSharedPointer>
#include <QTreeWidgetItem>
#include <set>
#include <unordered_map>
class QPainterPath;
@ -388,7 +389,6 @@ class PDFFontCache
{
public:
inline explicit PDFFontCache(size_t fontCacheLimit, size_t realizedFontCacheLimit) :
m_fontCacheShrinkEnabled(true),
m_fontCacheLimit(fontCacheLimit),
m_realizedFontCacheLimit(realizedFontCacheLimit),
m_document(nullptr)
@ -414,7 +414,12 @@ public:
/// Sets or unsets font shrinking (i.e. font can be deleted from the cache). In multithreading environment,
/// font deletion is not thread safe. For this reason, disable font deletion by calling this function.
void setCacheShrinkEnabled(bool enabled);
/// First parameter, \p source determines which object enables cache shrinking (so some objects can
/// enable shrinking, while some objects will disable it). Only if all objects enables cache shrinking,
/// then cache can shrink.
/// \param source Source object
/// \param enabled Enable or disable cache shrinking
void setCacheShrinkEnabled(void* source, bool enabled);
/// Set font cache limits
void setCacheLimits(int fontCacheLimit, int instancedFontCacheLimit);
@ -423,13 +428,13 @@ public:
void shrink();
private:
bool m_fontCacheShrinkEnabled;
size_t m_fontCacheLimit;
size_t m_realizedFontCacheLimit;
mutable QMutex m_mutex;
const PDFDocument* m_document;
mutable std::map<PDFObjectReference, PDFFontPointer> m_fontCache;
mutable std::map<std::pair<PDFFontPointer, PDFReal>, PDFRealizedFontPointer> m_realizedFontCache;
mutable std::set<void*> m_fontCacheShrinkDisabledObjects;
};
/// Performs mapping from CID to GID (even identity mapping, if byte array is empty)

View File

@ -20,7 +20,13 @@
namespace pdf
{
void PDFProgress::start(size_t stepCount)
PDFProgress::PDFProgress(QObject* parent) :
QObject(parent)
{
qRegisterMetaType<pdf::ProgressStartupInfo>();
}
void PDFProgress::start(size_t stepCount, ProgressStartupInfo startupInfo)
{
Q_ASSERT(stepCount > 0);
@ -28,7 +34,7 @@ void PDFProgress::start(size_t stepCount)
m_stepCount = stepCount;
m_percentage = 0;
emit progressStarted();
emit progressStarted(qMove(startupInfo));
}
void PDFProgress::step()

View File

@ -27,19 +27,25 @@
namespace pdf
{
struct ProgressStartupInfo
{
bool showDialog = false;
QString text;
};
class PDFFORQTLIBSHARED_EXPORT PDFProgress : public QObject
{
Q_OBJECT
public:
explicit PDFProgress(QObject* parent) : QObject(parent) { }
explicit PDFProgress(QObject* parent);
void start(size_t stepCount);
void start(size_t stepCount, ProgressStartupInfo startupInfo);
void step();
void finish();
signals:
void progressStarted();
void progressStarted(ProgressStartupInfo info);
void progressStep(int percentage);
void progressFinished();
@ -49,6 +55,8 @@ private:
std::atomic<int> m_percentage = 0;
};
Q_DECLARE_METATYPE(ProgressStartupInfo)
} // namespace pdf
#endif // PDFPROGRESS_H

View File

@ -73,7 +73,9 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_isLoadingUI(false),
m_progress(new pdf::PDFProgress(this)),
m_taskbarButton(new QWinTaskbarButton(this)),
m_progressTaskbarIndicator(nullptr)
m_progressTaskbarIndicator(nullptr),
m_progressDialog(nullptr),
m_isBusy(false)
{
ui->setupUi(this);
@ -159,6 +161,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
setCentralWidget(m_pdfWidget);
setFocusProxy(m_pdfWidget);
m_pdfWidget->updateCacheLimits(m_settings->getCompiledPageCacheLimit() * 1024, m_settings->getThumbnailsCacheLimit(), m_settings->getFontCacheLimit(), m_settings->getInstancedFontCacheLimit());
m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress);
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
@ -507,21 +510,46 @@ void PDFViewerMainWindow::onActionTriggered(const pdf::PDFAction* action)
}
}
void PDFViewerMainWindow::onProgressStarted()
void PDFViewerMainWindow::onProgressStarted(pdf::ProgressStartupInfo info)
{
Q_ASSERT(!m_progressDialog);
if (info.showDialog)
{
m_progressDialog = new QProgressDialog(info.text, QString(), 0, 100, this);
m_progressDialog->setWindowModality(Qt::WindowModal);
m_progressDialog->setCancelButton(nullptr);
}
m_progressTaskbarIndicator->setRange(0, 100);
m_progressTaskbarIndicator->reset();
m_progressTaskbarIndicator->show();
m_isBusy = true;
updateActionsAvailability();
}
void PDFViewerMainWindow::onProgressStep(int percentage)
{
if (m_progressDialog)
{
m_progressDialog->setValue(percentage);
}
m_progressTaskbarIndicator->setValue(percentage);
}
void PDFViewerMainWindow::onProgressFinished()
{
if (m_progressDialog)
{
m_progressDialog->hide();
m_progressDialog->deleteLater();
m_progressDialog = nullptr;
}
m_progressTaskbarIndicator->hide();
m_isBusy = false;
updateActionsAvailability();
}
void PDFViewerMainWindow::readSettings()
@ -697,19 +725,20 @@ void PDFViewerMainWindow::updateUI(bool fullUpdate)
void PDFViewerMainWindow::updateActionsAvailability()
{
const bool isReading = m_futureWatcher.isRunning();
const bool isBusy = m_futureWatcher.isRunning() || m_isBusy;
const bool hasDocument = m_pdfDocument;
const bool hasValidDocument = !isReading && hasDocument;
const bool hasValidDocument = !isBusy && hasDocument;
ui->actionOpen->setEnabled(!isReading);
ui->actionOpen->setEnabled(!isBusy);
ui->actionClose->setEnabled(hasValidDocument);
ui->actionQuit->setEnabled(!isReading);
ui->actionOptions->setEnabled(!isReading);
ui->actionAbout->setEnabled(!isReading);
ui->actionQuit->setEnabled(!isBusy);
ui->actionOptions->setEnabled(!isBusy);
ui->actionAbout->setEnabled(!isBusy);
ui->actionFitPage->setEnabled(hasValidDocument);
ui->actionFitWidth->setEnabled(hasValidDocument);
ui->actionFitHeight->setEnabled(hasValidDocument);
ui->actionRendering_Errors->setEnabled(hasValidDocument);
setEnabled(!isBusy);
}
void PDFViewerMainWindow::onViewerSettingsChanged()

View File

@ -33,6 +33,7 @@
#include <QWinTaskbarButton>
#include <QWinTaskbarProgress>
#include <QFutureWatcher>
#include <QProgressDialog>
class QLabel;
class QSpinBox;
@ -99,7 +100,7 @@ private:
void onPageZoomSpinboxEditingFinished();
void onActionTriggered(const pdf::PDFAction* action);
void onProgressStarted();
void onProgressStarted(pdf::ProgressStartupInfo info);
void onProgressStep(int percentage);
void onProgressFinished();
@ -156,6 +157,9 @@ private:
QFuture<AsyncReadingResult> m_future;
QFutureWatcher<AsyncReadingResult> m_futureWatcher;
QProgressDialog* m_progressDialog;
bool m_isBusy;
};
} // namespace pdfviewer