diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2022-05-30 17:36:55 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2023-02-16 06:57:23 +0100 |
commit | b8419577eb99c1589768d55ddfdc136818df87ae (patch) | |
tree | 64c90b32d6d6aea84f01c2300f90e38b42bc5b3c | |
parent | b385e76b1fc60ce1f48d2b805a4a2d2a2b23d1bd (diff) |
Add QPdfPageSelector
This is a QSpinBox subclass that shows PDF page "labels" rather than a
1-based or 0-based page index. If a book starts with Roman numerals in
the preface, and then page 1 is the first page of Chapter 1, the spinbox
should be in sync with the page numbers as printed on the pages, and
with the labels that will eventually be shown under the thumbnails
on the Pages sidebar tab.
On the other hand, the user probably needs to see the 1-based page index
somewhere, at least to be able to make sense of the print dialog,
because there the range of pages to print will be 1-based. So we put
the page index into the title bar: title, page label, index, count.
Task-number: QTBUG-102271
Change-Id: Ic461094ba4caae3067409f7f436bd4e7504a4bdb
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
-rw-r--r-- | examples/pdfwidgets/pdfviewer/mainwindow.cpp | 17 | ||||
-rw-r--r-- | examples/pdfwidgets/pdfviewer/mainwindow.h | 3 | ||||
-rw-r--r-- | src/pdf/qpdfdocument.cpp | 17 | ||||
-rw-r--r-- | src/pdf/qpdfdocument.h | 1 | ||||
-rw-r--r-- | src/pdfwidgets/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/pdfwidgets/qpdfpageselector.cpp | 105 | ||||
-rw-r--r-- | src/pdfwidgets/qpdfpageselector.h | 44 | ||||
-rw-r--r-- | src/pdfwidgets/qpdfpageselector_p.h | 42 |
8 files changed, 222 insertions, 8 deletions
diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.cpp b/examples/pdfwidgets/pdfviewer/mainwindow.cpp index 02caffa48..d7527a8d5 100644 --- a/examples/pdfwidgets/pdfviewer/mainwindow.cpp +++ b/examples/pdfwidgets/pdfviewer/mainwindow.cpp @@ -8,10 +8,10 @@ #include <QFileDialog> #include <QMessageBox> -#include <QSpinBox> #include <QPdfBookmarkModel> #include <QPdfDocument> #include <QPdfPageNavigator> +#include <QPdfPageSelector> #include <QStandardPaths> #include <QtMath> @@ -23,7 +23,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_zoomSelector(new ZoomSelector(this)) - , m_pageSelector(new QSpinBox(this)) + , m_pageSelector(new QPdfPageSelector(this)) , m_document(new QPdfDocument(this)) { ui->setupUi(this); @@ -32,9 +32,10 @@ MainWindow::MainWindow(QWidget *parent) ui->mainToolBar->insertWidget(ui->actionZoom_In, m_zoomSelector); ui->mainToolBar->insertWidget(ui->actionForward, m_pageSelector); - connect(m_pageSelector, &QSpinBox::valueChanged, this, &MainWindow::pageSelected); + connect(m_pageSelector, &QPdfPageSelector::valueChanged, this, &MainWindow::pageSelected); + m_pageSelector->setDocument(m_document); auto nav = ui->pdfView->pageNavigator(); - connect(nav, &QPdfPageNavigator::currentPageChanged, m_pageSelector, &QSpinBox::setValue); + connect(nav, &QPdfPageNavigator::currentPageChanged, m_pageSelector, &QPdfPageSelector::setValue); connect(nav, &QPdfPageNavigator::backAvailableChanged, ui->actionBack, &QAction::setEnabled); connect(nav, &QPdfPageNavigator::forwardAvailableChanged, ui->actionForward, &QAction::setEnabled); @@ -65,10 +66,7 @@ void MainWindow::open(const QUrl &docLocation) { if (docLocation.isLocalFile()) { m_document->load(docLocation.toLocalFile()); - const auto documentTitle = m_document->metaData(QPdfDocument::MetaDataField::Title).toString(); - setWindowTitle(!documentTitle.isEmpty() ? documentTitle : QStringLiteral("PDF Viewer")); pageSelected(0); - m_pageSelector->setMaximum(m_document->pageCount() - 1); } else { const QString message = tr("%1 is not a valid local file").arg(docLocation.toString()); qCDebug(lcExample).noquote() << message; @@ -91,6 +89,11 @@ void MainWindow::pageSelected(int page) { auto nav = ui->pdfView->pageNavigator(); nav->jump(page, {}, nav->currentZoom()); + const auto documentTitle = m_document->metaData(QPdfDocument::MetaDataField::Title).toString(); + setWindowTitle(!documentTitle.isEmpty() ? documentTitle : QStringLiteral("PDF Viewer")); + setWindowTitle(tr("%1: page %2 (%3 of %4)") + .arg(documentTitle.isEmpty() ? u"PDF Viewer"_qs : documentTitle, + m_pageSelector->text(), QString::number(page + 1), QString::number(m_document->pageCount()))); } void MainWindow::on_actionOpen_triggered() diff --git a/examples/pdfwidgets/pdfviewer/mainwindow.h b/examples/pdfwidgets/pdfviewer/mainwindow.h index 958e11061..2827fb2b6 100644 --- a/examples/pdfwidgets/pdfviewer/mainwindow.h +++ b/examples/pdfwidgets/pdfviewer/mainwindow.h @@ -16,6 +16,7 @@ class MainWindow; class QFileDialog; class QPdfDocument; +class QPdfPageSelector; class QPdfView; class QSpinBox; QT_END_NAMESPACE @@ -53,7 +54,7 @@ private slots: private: Ui::MainWindow *ui; ZoomSelector *m_zoomSelector; - QSpinBox *m_pageSelector; + QPdfPageSelector *m_pageSelector; QFileDialog *m_fileDialog = nullptr; QPdfDocument *m_document; diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp index 268389f4a..a6aed4bfe 100644 --- a/src/pdf/qpdfdocument.cpp +++ b/src/pdf/qpdfdocument.cpp @@ -783,6 +783,8 @@ QAbstractListModel *QPdfDocument::pageModel() If the document does not have custom page numbering, this function returns \c {page + 1}. + + \sa pageIndexForLabel() */ QString QPdfDocument::pageLabel(int page) { @@ -797,6 +799,21 @@ QString QPdfDocument::pageLabel(int page) } /*! + Returns the index of the page that has the \a label, or \c -1 if not found. + + \sa pageLabel() +*/ +int QPdfDocument::pageIndexForLabel(const QString &label) +{ + const auto trimmed = label.trimmed(); + for (int i = 0; i < d->pageCount; ++i) { + if (pageLabel(i) == trimmed) + return i; + } + return -1; +} + +/*! Renders the \a page into a QImage of size \a imageSize according to the provided \a renderOptions. diff --git a/src/pdf/qpdfdocument.h b/src/pdf/qpdfdocument.h index 5f55ed29c..8355246ae 100644 --- a/src/pdf/qpdfdocument.h +++ b/src/pdf/qpdfdocument.h @@ -89,6 +89,7 @@ public: Q_INVOKABLE QSizeF pagePointSize(int page) const; Q_INVOKABLE QString pageLabel(int page); + Q_INVOKABLE int pageIndexForLabel(const QString &label); QAbstractListModel *pageModel(); diff --git a/src/pdfwidgets/CMakeLists.txt b/src/pdfwidgets/CMakeLists.txt index d374315e3..592409b66 100644 --- a/src/pdfwidgets/CMakeLists.txt +++ b/src/pdfwidgets/CMakeLists.txt @@ -5,6 +5,7 @@ find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Core Gui Widgets) qt_internal_add_module(PdfWidgets SOURCES + qpdfpageselector.cpp qpdfpageselector.h qpdfpageselector_p.h qpdfview.cpp qpdfview.h qpdfview_p.h qtpdfwidgetsglobal.h LIBRARIES diff --git a/src/pdfwidgets/qpdfpageselector.cpp b/src/pdfwidgets/qpdfpageselector.cpp new file mode 100644 index 000000000..72ab28355 --- /dev/null +++ b/src/pdfwidgets/qpdfpageselector.cpp @@ -0,0 +1,105 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qpdfpageselector.h" +#include "qpdfpageselector_p.h" + +#include <QPdfDocument> + +QT_BEGIN_NAMESPACE + +QPdfPageSelectorPrivate::QPdfPageSelectorPrivate(QPdfPageSelector *q) + : q_ptr(q) +{ +} + +void QPdfPageSelectorPrivate::documentStatusChanged() +{ + Q_Q(QPdfPageSelector); + if (!document.isNull() && document->status() == QPdfDocument::Status::Ready) { + q->setMaximum(document->pageCount()); + q->setValue(0); + } +} + +/*! + \class QPdfPageSelector + \inmodule QtPdf + \brief A QSpinBox for selecting a PDF page. + + QPdfPageSelector is a QSpinBox specialized for selecting a page label + from a QPdfDocument. + + \sa QPdfDocument::pageLabel() +*/ + +/*! + Constructs a PDF page selector with parent widget \a parent. +*/ +QPdfPageSelector::QPdfPageSelector(QWidget *parent) + : QSpinBox(parent) + , d_ptr(new QPdfPageSelectorPrivate(this)) +{ +} + +/*! + Destroys the page selector. +*/ +QPdfPageSelector::~QPdfPageSelector() +{ +} + +/*! + \property QPdfPageSelector::document + + This property holds the document to be viewed. +*/ +void QPdfPageSelector::setDocument(QPdfDocument *document) +{ + Q_D(QPdfPageSelector); + + if (d->document == document) + return; + + if (d->document) + disconnect(d->documentStatusChangedConnection); + + d->document = document; + emit documentChanged(d->document); + + if (d->document) + d->documentStatusChangedConnection = + connect(d->document.data(), &QPdfDocument::statusChanged, this, + [d](){ d->documentStatusChanged(); }); + + d->documentStatusChanged(); +} + +QPdfDocument *QPdfPageSelector::document() const +{ + Q_D(const QPdfPageSelector); + + return d->document; +} + +int QPdfPageSelector::valueFromText(const QString &text) const +{ + Q_D(const QPdfPageSelector); + if (d->document.isNull()) + return 0; + + return d->document->pageIndexForLabel(text); +} + +QString QPdfPageSelector::textFromValue(int value) const +{ + Q_D(const QPdfPageSelector); + if (d->document.isNull()) + return {}; + + return d->document->pageLabel(value); +} + +QT_END_NAMESPACE + +#include "moc_qpdfpageselector.cpp" diff --git a/src/pdfwidgets/qpdfpageselector.h b/src/pdfwidgets/qpdfpageselector.h new file mode 100644 index 000000000..bc2e352f7 --- /dev/null +++ b/src/pdfwidgets/qpdfpageselector.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFPAGESELECTOR_H +#define QPDFPAGESELECTOR_H + +#include <QtPdfWidgets/qtpdfwidgetsglobal.h> +#include <QtWidgets/qspinbox.h> + +QT_BEGIN_NAMESPACE + +class QPdfDocument; +class QPdfPageNavigator; +class QPdfPageSelectorPrivate; + +class Q_PDF_WIDGETS_EXPORT QPdfPageSelector : public QSpinBox +{ + Q_OBJECT + + Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) + +public: + QPdfPageSelector() : QPdfPageSelector(nullptr) {} + explicit QPdfPageSelector(QWidget *parent); + ~QPdfPageSelector(); + + void setDocument(QPdfDocument *document); + QPdfDocument *document() const; + +Q_SIGNALS: + void documentChanged(QPdfDocument *document); + +protected: + int valueFromText(const QString &text) const override; + QString textFromValue(int value) const override; + +private: + Q_DECLARE_PRIVATE(QPdfPageSelector) + QScopedPointer<QPdfPageSelectorPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QPDFPAGESELECTOR_H diff --git a/src/pdfwidgets/qpdfpageselector_p.h b/src/pdfwidgets/qpdfpageselector_p.h new file mode 100644 index 000000000..b2cd6ecf7 --- /dev/null +++ b/src/pdfwidgets/qpdfpageselector_p.h @@ -0,0 +1,42 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFPAGESELECTOR_P_H +#define QPDFPAGESELECTOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpdfpageselector.h" + +#include <QPointer> + +QT_BEGIN_NAMESPACE + +class QPdfPageRenderer; + +class QPdfPageSelectorPrivate +{ + Q_DECLARE_PUBLIC(QPdfPageSelector) + +public: + QPdfPageSelectorPrivate(QPdfPageSelector *q); + + void documentStatusChanged(); + + QPdfPageSelector *q_ptr; + QPointer<QPdfDocument> document; + QMetaObject::Connection documentStatusChangedConnection; +}; + +QT_END_NAMESPACE + +#endif // QPDFPAGESELECTOR_P_H |