mirror of
https://github.com/YACReader/yacreader
synced 2026-04-12 15:49:53 -04:00
Implement continuous scrolling
This commit is contained in:
@ -62,6 +62,7 @@ HEADERS += ../common/comic.h \
|
|||||||
goto_dialog.h \
|
goto_dialog.h \
|
||||||
magnifying_glass.h \
|
magnifying_glass.h \
|
||||||
main_window_viewer.h \
|
main_window_viewer.h \
|
||||||
|
continuous_page_widget.h \
|
||||||
mouse_handler.h \
|
mouse_handler.h \
|
||||||
viewer.h \
|
viewer.h \
|
||||||
options_dialog.h \
|
options_dialog.h \
|
||||||
@ -98,6 +99,7 @@ SOURCES += ../common/comic.cpp \
|
|||||||
goto_dialog.cpp \
|
goto_dialog.cpp \
|
||||||
magnifying_glass.cpp \
|
magnifying_glass.cpp \
|
||||||
main_window_viewer.cpp \
|
main_window_viewer.cpp \
|
||||||
|
continuous_page_widget.cpp \
|
||||||
mouse_handler.cpp \
|
mouse_handler.cpp \
|
||||||
viewer.cpp \
|
viewer.cpp \
|
||||||
options_dialog.cpp \
|
options_dialog.cpp \
|
||||||
|
|||||||
@ -82,6 +82,8 @@ public:
|
|||||||
void setDoublePage(bool b) { settings->setValue(DOUBLE_PAGE, b); }
|
void setDoublePage(bool b) { settings->setValue(DOUBLE_PAGE, b); }
|
||||||
bool getDoubleMangaPage() { return settings->value(DOUBLE_MANGA_PAGE).toBool(); }
|
bool getDoubleMangaPage() { return settings->value(DOUBLE_MANGA_PAGE).toBool(); }
|
||||||
void setDoubleMangaPage(bool b) { settings->setValue(DOUBLE_MANGA_PAGE, b); }
|
void setDoubleMangaPage(bool b) { settings->setValue(DOUBLE_MANGA_PAGE, b); }
|
||||||
|
bool getContinuousScroll() { return settings->value(CONTINUOUS_SCROLL, false).toBool(); }
|
||||||
|
void setContinuousScroll(bool b) { settings->setValue(CONTINUOUS_SCROLL, b); }
|
||||||
bool getEnlargeImages() { return settings->value(ENLARGE_IMAGES, true).toBool(); }
|
bool getEnlargeImages() { return settings->value(ENLARGE_IMAGES, true).toBool(); }
|
||||||
void setEnlargeImages(bool b) { settings->setValue(ENLARGE_IMAGES, b); }
|
void setEnlargeImages(bool b) { settings->setValue(ENLARGE_IMAGES, b); }
|
||||||
|
|
||||||
|
|||||||
393
YACReader/continuous_page_widget.cpp
Normal file
393
YACReader/continuous_page_widget.cpp
Normal file
@ -0,0 +1,393 @@
|
|||||||
|
#include "continuous_page_widget.h"
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
ContinuousPageWidget::ContinuousPageWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
|
sp.setHeightForWidth(true);
|
||||||
|
setSizePolicy(sp);
|
||||||
|
setMouseTracking(true);
|
||||||
|
initTheme(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::applyTheme(const Theme &)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::setRender(Render *r)
|
||||||
|
{
|
||||||
|
render = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::setNumPages(int count)
|
||||||
|
{
|
||||||
|
numPages = count;
|
||||||
|
defaultPageSize = QSize(800, 1200);
|
||||||
|
pageSizes.fill(QSize(0, 0), count);
|
||||||
|
relayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::setZoomFactor(int zoom)
|
||||||
|
{
|
||||||
|
if (zoomFactor == zoom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zoomFactor = zoom;
|
||||||
|
relayout(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::probeBufferedPages()
|
||||||
|
{
|
||||||
|
if (!render || numPages == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
for (int i = 0; i < numPages; ++i) {
|
||||||
|
const QImage *img = render->bufferedImage(i);
|
||||||
|
bool hasKnownSize = pageSizes[i].width() > 0 && pageSizes[i].height() > 0;
|
||||||
|
if (img && !img->isNull() && !hasKnownSize) {
|
||||||
|
pageSizes[i] = img->size();
|
||||||
|
if (defaultPageSize == QSize(800, 1200)) {
|
||||||
|
defaultPageSize = img->size();
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
relayout(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::reset()
|
||||||
|
{
|
||||||
|
numPages = 0;
|
||||||
|
pageSizes.clear();
|
||||||
|
yPositions.clear();
|
||||||
|
currentTotalHeight = 0;
|
||||||
|
layoutSnapshot = LayoutSnapshot();
|
||||||
|
defaultPageSize = QSize(800, 1200);
|
||||||
|
setMinimumHeight(0);
|
||||||
|
setMaximumHeight(QWIDGETSIZE_MAX);
|
||||||
|
updateGeometry();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::centerPage(int scrollY, int viewportHeight) const
|
||||||
|
{
|
||||||
|
const int centerY = scrollY + std::max(0, viewportHeight / 2);
|
||||||
|
return pageAtY(centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::yPositionForPage(int pageIndex) const
|
||||||
|
{
|
||||||
|
if (pageIndex < 0 || pageIndex >= yPositions.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return yPositions[pageIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::totalHeight() const
|
||||||
|
{
|
||||||
|
return currentTotalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContinuousPageWidget::hasHeightForWidth() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::heightForWidth(int w) const
|
||||||
|
{
|
||||||
|
if (numPages == 0 || w <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h = 0;
|
||||||
|
for (int i = 0; i < numPages; ++i) {
|
||||||
|
QSize scaled = scaledPageSize(i, w);
|
||||||
|
h += scaled.height();
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ContinuousPageWidget::sizeHint() const
|
||||||
|
{
|
||||||
|
return QSize(defaultPageSize.width(), currentTotalHeight > 0 ? currentTotalHeight : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::onPageAvailable(int absolutePageIndex)
|
||||||
|
{
|
||||||
|
if (!render || absolutePageIndex < 0 || absolutePageIndex >= numPages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QImage *img = render->bufferedImage(absolutePageIndex);
|
||||||
|
if (!img || img->isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize naturalSize = img->size();
|
||||||
|
|
||||||
|
// update default page size from the first real page we see
|
||||||
|
if (defaultPageSize == QSize(800, 1200) && !naturalSize.isNull()) {
|
||||||
|
defaultPageSize = naturalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sizeChanged = (pageSizes[absolutePageIndex] != naturalSize);
|
||||||
|
pageSizes[absolutePageIndex] = naturalSize;
|
||||||
|
|
||||||
|
if (sizeChanged) {
|
||||||
|
// keep anchor page visually stable while refined page sizes arrive
|
||||||
|
relayout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// repaint the region where this page lives
|
||||||
|
if (absolutePageIndex < yPositions.size()) {
|
||||||
|
QSize scaled = scaledPageSize(absolutePageIndex, width());
|
||||||
|
QRect pageRect(0, yPositions[absolutePageIndex], scaled.width(), scaled.height());
|
||||||
|
update(pageRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
if (numPages == 0 || !render) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainter painter(this);
|
||||||
|
|
||||||
|
QRect visibleRect = event->rect();
|
||||||
|
int firstPage = pageAtY(visibleRect.top());
|
||||||
|
int lastPage = pageAtY(visibleRect.bottom());
|
||||||
|
|
||||||
|
int w = width();
|
||||||
|
for (int i = firstPage; i <= lastPage && i < numPages; ++i) {
|
||||||
|
int y = yPositions[i];
|
||||||
|
QSize scaled = scaledPageSize(i, w);
|
||||||
|
// center horizontally if page is narrower than widget
|
||||||
|
int x = (w - scaled.width()) / 2;
|
||||||
|
if (x < 0) {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
QRect pageRect(x, y, scaled.width(), scaled.height());
|
||||||
|
|
||||||
|
const QImage *img = render->bufferedImage(i);
|
||||||
|
if (img && !img->isNull()) {
|
||||||
|
if (img->size() != scaled) {
|
||||||
|
painter.drawImage(pageRect, img->scaled(scaled, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||||
|
} else {
|
||||||
|
painter.drawImage(pageRect, *img);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// placeholder
|
||||||
|
painter.fillRect(pageRect, QColor(45, 45, 45));
|
||||||
|
painter.setPen(theme.viewer.defaultTextColor);
|
||||||
|
painter.drawText(pageRect, Qt::AlignCenter, tr("Loading page %1").arg(i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
relayout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::setAnchorPage(int page)
|
||||||
|
{
|
||||||
|
anchorPage = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::setViewportState(int scrollY, int viewportHeight)
|
||||||
|
{
|
||||||
|
viewportScrollY = std::max(0, scrollY);
|
||||||
|
currentViewportHeight = std::max(0, viewportHeight);
|
||||||
|
hasViewportState = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::updateLayout()
|
||||||
|
{
|
||||||
|
relayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::updateLayoutWithAnchor()
|
||||||
|
{
|
||||||
|
relayout(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContinuousPageWidget::relayout(bool preserveAnchor)
|
||||||
|
{
|
||||||
|
int w = width();
|
||||||
|
if (w <= 0) {
|
||||||
|
w = parentWidget() ? parentWidget()->width() : 0;
|
||||||
|
}
|
||||||
|
if (w <= 0) {
|
||||||
|
w = defaultPageSize.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
const LayoutSnapshot oldSnapshot = layoutSnapshot;
|
||||||
|
|
||||||
|
ViewportAnchor anchor;
|
||||||
|
if (preserveAnchor && hasViewportState && !oldSnapshot.yPositions.isEmpty()) {
|
||||||
|
anchor = anchorFromViewport(oldSnapshot, viewportScrollY, currentViewportHeight);
|
||||||
|
} else if (preserveAnchor && anchorPage >= 0) {
|
||||||
|
anchor.pageIndex = anchorPage;
|
||||||
|
anchor.offsetRatio = 0.5f;
|
||||||
|
anchor.valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutSnapshot = buildLayoutSnapshot(w);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
Q_ASSERT(layoutSnapshot.yPositions.size() == numPages);
|
||||||
|
Q_ASSERT(layoutSnapshot.scaledSizes.size() == numPages);
|
||||||
|
for (int i = 0; i < layoutSnapshot.scaledSizes.size(); ++i) {
|
||||||
|
Q_ASSERT(layoutSnapshot.scaledSizes[i].width() > 0);
|
||||||
|
Q_ASSERT(layoutSnapshot.scaledSizes[i].height() > 0);
|
||||||
|
if (i > 0) {
|
||||||
|
Q_ASSERT(layoutSnapshot.yPositions[i] >= layoutSnapshot.yPositions[i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
yPositions = layoutSnapshot.yPositions;
|
||||||
|
currentTotalHeight = layoutSnapshot.totalHeight;
|
||||||
|
|
||||||
|
setFixedHeight(currentTotalHeight);
|
||||||
|
updateGeometry();
|
||||||
|
|
||||||
|
if (!preserveAnchor || !anchor.valid || currentViewportHeight <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int newScrollForAnchor = resolveAnchorToScrollY(layoutSnapshot, anchor, currentViewportHeight);
|
||||||
|
emit layoutScrollPositionRequested(newScrollForAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinuousPageWidget::LayoutSnapshot ContinuousPageWidget::buildLayoutSnapshot(int w) const
|
||||||
|
{
|
||||||
|
LayoutSnapshot snapshot;
|
||||||
|
|
||||||
|
if (numPages <= 0 || w <= 0) {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot.yPositions.resize(numPages);
|
||||||
|
snapshot.scaledSizes.resize(numPages);
|
||||||
|
|
||||||
|
qint64 y = 0;
|
||||||
|
for (int i = 0; i < numPages; ++i) {
|
||||||
|
snapshot.yPositions[i] = static_cast<int>(std::min<qint64>(y, std::numeric_limits<int>::max()));
|
||||||
|
QSize scaled = scaledPageSize(i, w);
|
||||||
|
scaled.setWidth(std::max(1, scaled.width()));
|
||||||
|
scaled.setHeight(std::max(1, scaled.height()));
|
||||||
|
snapshot.scaledSizes[i] = scaled;
|
||||||
|
y += scaled.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot.totalHeight = static_cast<int>(std::min<qint64>(y, static_cast<qint64>(QWIDGETSIZE_MAX)));
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::pageAtY(const LayoutSnapshot &snapshot, int y) const
|
||||||
|
{
|
||||||
|
if (snapshot.yPositions.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::upper_bound(snapshot.yPositions.constBegin(), snapshot.yPositions.constEnd(), y);
|
||||||
|
if (it == snapshot.yPositions.constBegin()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
--it;
|
||||||
|
return static_cast<int>(it - snapshot.yPositions.constBegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinuousPageWidget::ViewportAnchor ContinuousPageWidget::anchorFromViewport(const LayoutSnapshot &snapshot, int scrollY, int viewportHeight) const
|
||||||
|
{
|
||||||
|
ViewportAnchor anchor;
|
||||||
|
|
||||||
|
if (snapshot.yPositions.isEmpty() || viewportHeight <= 0) {
|
||||||
|
return anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int maxScroll = std::max(0, snapshot.totalHeight - viewportHeight);
|
||||||
|
const int clampedScroll = qBound(0, scrollY, maxScroll);
|
||||||
|
const int anchorY = clampedScroll + viewportHeight / 2;
|
||||||
|
const int page = pageAtY(snapshot, anchorY);
|
||||||
|
|
||||||
|
if (page < 0 || page >= snapshot.scaledSizes.size()) {
|
||||||
|
return anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pageTop = snapshot.yPositions[page];
|
||||||
|
const int pageHeight = std::max(1, snapshot.scaledSizes[page].height());
|
||||||
|
const float ratio = static_cast<float>(anchorY - pageTop) / static_cast<float>(pageHeight);
|
||||||
|
|
||||||
|
anchor.pageIndex = page;
|
||||||
|
anchor.offsetRatio = qBound(0.0f, ratio, 1.0f);
|
||||||
|
anchor.valid = true;
|
||||||
|
return anchor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::resolveAnchorToScrollY(const LayoutSnapshot &snapshot, const ViewportAnchor &anchor, int viewportHeight) const
|
||||||
|
{
|
||||||
|
if (!anchor.valid || viewportHeight <= 0 || snapshot.yPositions.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anchor.pageIndex < 0 || anchor.pageIndex >= snapshot.yPositions.size() || anchor.pageIndex >= snapshot.scaledSizes.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int pageTop = snapshot.yPositions[anchor.pageIndex];
|
||||||
|
const int pageHeight = std::max(1, snapshot.scaledSizes[anchor.pageIndex].height());
|
||||||
|
const int anchorY = pageTop + qRound(anchor.offsetRatio * pageHeight);
|
||||||
|
const int maxScroll = std::max(0, snapshot.totalHeight - viewportHeight);
|
||||||
|
const int target = anchorY - viewportHeight / 2;
|
||||||
|
return qBound(0, target, maxScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContinuousPageWidget::pageAtY(int y) const
|
||||||
|
{
|
||||||
|
return pageAtY(layoutSnapshot, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ContinuousPageWidget::scaledPageSize(int pageIndex, int forWidth) const
|
||||||
|
{
|
||||||
|
QSize natural = (pageIndex < pageSizes.size() && pageSizes[pageIndex].width() > 0 && pageSizes[pageIndex].height() > 0)
|
||||||
|
? pageSizes[pageIndex]
|
||||||
|
: defaultPageSize;
|
||||||
|
|
||||||
|
float scale = scaleForPage(pageIndex, forWidth);
|
||||||
|
int scaledW = std::max(1, qRound(natural.width() * scale));
|
||||||
|
int scaledH = std::max(1, qRound(natural.height() * scale));
|
||||||
|
return QSize(scaledW, scaledH);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ContinuousPageWidget::scaleForPage(int pageIndex, int forWidth) const
|
||||||
|
{
|
||||||
|
QSize natural = (pageIndex < pageSizes.size() && pageSizes[pageIndex].width() > 0 && pageSizes[pageIndex].height() > 0)
|
||||||
|
? pageSizes[pageIndex]
|
||||||
|
: defaultPageSize;
|
||||||
|
|
||||||
|
if (natural.width() <= 0 || forWidth <= 0) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float baseScale = static_cast<float>(forWidth) / natural.width();
|
||||||
|
float zoomMultiplier = zoomFactor / 100.0f;
|
||||||
|
return baseScale * zoomMultiplier;
|
||||||
|
}
|
||||||
86
YACReader/continuous_page_widget.h
Normal file
86
YACReader/continuous_page_widget.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#ifndef CONTINUOUS_PAGE_WIDGET_H
|
||||||
|
#define CONTINUOUS_PAGE_WIDGET_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "themable.h"
|
||||||
|
|
||||||
|
class Render;
|
||||||
|
|
||||||
|
class ContinuousPageWidget : public QWidget, protected Themable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ContinuousPageWidget(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setRender(Render *r);
|
||||||
|
void setNumPages(int count);
|
||||||
|
void setZoomFactor(int zoom);
|
||||||
|
void probeBufferedPages();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
int centerPage(int scrollY, int viewportHeight) const;
|
||||||
|
int yPositionForPage(int pageIndex) const;
|
||||||
|
int totalHeight() const;
|
||||||
|
|
||||||
|
bool hasHeightForWidth() const override;
|
||||||
|
int heightForWidth(int w) const override;
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
void setAnchorPage(int page);
|
||||||
|
void setViewportState(int scrollY, int viewportHeight);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// emitted after layout recomputation when the preserved viewport anchor
|
||||||
|
// resolves to an absolute scroll position
|
||||||
|
void layoutScrollPositionRequested(int scrollY);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onPageAvailable(int absolutePageIndex);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
void applyTheme(const Theme &theme) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct LayoutSnapshot {
|
||||||
|
QVector<int> yPositions;
|
||||||
|
QVector<QSize> scaledSizes;
|
||||||
|
int totalHeight = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ViewportAnchor {
|
||||||
|
int pageIndex = -1;
|
||||||
|
float offsetRatio = 0.0f;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateLayout();
|
||||||
|
void updateLayoutWithAnchor();
|
||||||
|
void relayout(bool preserveAnchor);
|
||||||
|
LayoutSnapshot buildLayoutSnapshot(int w) const;
|
||||||
|
int pageAtY(const LayoutSnapshot &snapshot, int y) const;
|
||||||
|
ViewportAnchor anchorFromViewport(const LayoutSnapshot &snapshot, int scrollY, int viewportHeight) const;
|
||||||
|
int resolveAnchorToScrollY(const LayoutSnapshot &snapshot, const ViewportAnchor &anchor, int viewportHeight) const;
|
||||||
|
int pageAtY(int y) const;
|
||||||
|
QSize scaledPageSize(int pageIndex, int forWidth) const;
|
||||||
|
float scaleForPage(int pageIndex, int forWidth) const;
|
||||||
|
|
||||||
|
Render *render = nullptr;
|
||||||
|
int numPages = 0;
|
||||||
|
QVector<QSize> pageSizes;
|
||||||
|
QVector<int> yPositions;
|
||||||
|
int currentTotalHeight = 0;
|
||||||
|
LayoutSnapshot layoutSnapshot;
|
||||||
|
QSize defaultPageSize { 800, 1200 };
|
||||||
|
int zoomFactor = 100;
|
||||||
|
int anchorPage = -1;
|
||||||
|
int viewportScrollY = 0;
|
||||||
|
int currentViewportHeight = 0;
|
||||||
|
bool hasViewportState = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONTINUOUS_PAGE_WIDGET_H
|
||||||
@ -144,7 +144,7 @@ int main(int argc, char *argv[])
|
|||||||
QDir().mkpath(YACReader::getSettingsPath());
|
QDir().mkpath(YACReader::getSettingsPath());
|
||||||
|
|
||||||
Logger &logger = Logger::instance();
|
Logger &logger = Logger::instance();
|
||||||
logger.setLoggingLevel(QsLogging::InfoLevel);
|
logger.setLoggingLevel(QsLogging::TraceLevel);
|
||||||
|
|
||||||
if (parser.isSet("loglevel")) {
|
if (parser.isSet("loglevel")) {
|
||||||
if (parser.value("loglevel") == "trace") {
|
if (parser.value("loglevel") == "trace") {
|
||||||
|
|||||||
@ -77,6 +77,7 @@ MainWindowViewer::~MainWindowViewer()
|
|||||||
delete rightRotationAction;
|
delete rightRotationAction;
|
||||||
delete doublePageAction;
|
delete doublePageAction;
|
||||||
delete doubleMangaPageAction;
|
delete doubleMangaPageAction;
|
||||||
|
delete continuousScrollAction;
|
||||||
delete increasePageZoomAction;
|
delete increasePageZoomAction;
|
||||||
delete decreasePageZoomAction;
|
delete decreasePageZoomAction;
|
||||||
delete resetZoomAction;
|
delete resetZoomAction;
|
||||||
@ -309,28 +310,38 @@ void MainWindowViewer::createActions()
|
|||||||
fitToPageAction->setCheckable(true);
|
fitToPageAction->setCheckable(true);
|
||||||
connect(fitToPageAction, &QAction::triggered, this, &MainWindowViewer::fitToPageSwitch);
|
connect(fitToPageAction, &QAction::triggered, this, &MainWindowViewer::fitToPageSwitch);
|
||||||
|
|
||||||
|
continuousScrollAction = new QAction(tr("Continuous scroll"), this);
|
||||||
|
continuousScrollAction->setToolTip(tr("Switch to continuous scroll mode"));
|
||||||
|
continuousScrollAction->setIcon(QIcon(":/images/viewer_toolbar/toContinuousScroll.svg"));
|
||||||
|
continuousScrollAction->setCheckable(true);
|
||||||
|
continuousScrollAction->setChecked(Configuration::getConfiguration().getContinuousScroll());
|
||||||
|
connect(continuousScrollAction, &QAction::toggled, viewer, &Viewer::setContinuousScroll);
|
||||||
|
|
||||||
// fit modes have to be exclusive and checkable
|
// fit modes have to be exclusive and checkable
|
||||||
auto fitModes = new QActionGroup(this);
|
auto fitModes = new QActionGroup(this);
|
||||||
fitModes->addAction(adjustHeightAction);
|
fitModes->addAction(adjustHeightAction);
|
||||||
fitModes->addAction(adjustWidthAction);
|
fitModes->addAction(adjustWidthAction);
|
||||||
fitModes->addAction(adjustToFullSizeAction);
|
fitModes->addAction(adjustToFullSizeAction);
|
||||||
fitModes->addAction(fitToPageAction);
|
fitModes->addAction(fitToPageAction);
|
||||||
|
fitModes->addAction(continuousScrollAction);
|
||||||
|
|
||||||
switch (Configuration::getConfiguration().getFitMode()) {
|
if (Configuration::getConfiguration().getContinuousScroll()) {
|
||||||
case YACReader::FitMode::ToWidth:
|
continuousScrollAction->setChecked(true);
|
||||||
adjustWidthAction->setChecked(true);
|
} else {
|
||||||
break;
|
switch (Configuration::getConfiguration().getFitMode()) {
|
||||||
case YACReader::FitMode::ToHeight:
|
case YACReader::FitMode::ToWidth:
|
||||||
adjustHeightAction->setChecked(true);
|
adjustWidthAction->setChecked(true);
|
||||||
break;
|
break;
|
||||||
case YACReader::FitMode::FullRes:
|
case YACReader::FitMode::ToHeight:
|
||||||
adjustToFullSizeAction->setChecked(true);
|
adjustHeightAction->setChecked(true);
|
||||||
break;
|
break;
|
||||||
case YACReader::FitMode::FullPage:
|
case YACReader::FitMode::FullRes:
|
||||||
fitToPageAction->setChecked(true);
|
adjustToFullSizeAction->setChecked(true);
|
||||||
break;
|
break;
|
||||||
default:
|
case YACReader::FitMode::FullPage:
|
||||||
fitToPageAction->setChecked(true);
|
default:
|
||||||
|
fitToPageAction->setChecked(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resetZoomAction = new QAction(tr("Reset zoom"), this);
|
resetZoomAction = new QAction(tr("Reset zoom"), this);
|
||||||
@ -536,11 +547,14 @@ void MainWindowViewer::createToolBars()
|
|||||||
auto fitToPageTBAction = actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/fitToPage")), fitToPageAction);
|
auto fitToPageTBAction = actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/fitToPage")), fitToPageAction);
|
||||||
comicToolBar->addAction(fitToPageTBAction);
|
comicToolBar->addAction(fitToPageTBAction);
|
||||||
|
|
||||||
|
auto continuousScroollTBAction = actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/toContinuousScroll")), continuousScrollAction);
|
||||||
|
|
||||||
auto fitModes = new QActionGroup(this);
|
auto fitModes = new QActionGroup(this);
|
||||||
fitModes->addAction(adjustToWidthTBAction);
|
fitModes->addAction(adjustToWidthTBAction);
|
||||||
fitModes->addAction(adjustToHeightTBAction);
|
fitModes->addAction(adjustToHeightTBAction);
|
||||||
fitModes->addAction(adjustToFullSizeTBAction);
|
fitModes->addAction(adjustToFullSizeTBAction);
|
||||||
fitModes->addAction(fitToPageTBAction);
|
fitModes->addAction(fitToPageTBAction);
|
||||||
|
fitModes->addAction(continuousScroollTBAction);
|
||||||
|
|
||||||
zoomSliderAction = new YACReaderSlider(this);
|
zoomSliderAction = new YACReaderSlider(this);
|
||||||
zoomSliderAction->hide();
|
zoomSliderAction->hide();
|
||||||
@ -555,6 +569,7 @@ void MainWindowViewer::createToolBars()
|
|||||||
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/rotateR")), rightRotationAction));
|
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/rotateR")), rightRotationAction));
|
||||||
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/doublePage")), doublePageAction));
|
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/doublePage")), doublePageAction));
|
||||||
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/doubleMangaPage")), doubleMangaPageAction));
|
comicToolBar->addAction(actionWithCustomIcon(QIcon(addExtensionToIconPathInToolbar(":/images/viewer_toolbar/doubleMangaPage")), doubleMangaPageAction));
|
||||||
|
comicToolBar->addAction(continuousScroollTBAction);
|
||||||
|
|
||||||
comicToolBar->addSeparator();
|
comicToolBar->addSeparator();
|
||||||
|
|
||||||
@ -603,6 +618,7 @@ void MainWindowViewer::createToolBars()
|
|||||||
viewer->addAction(rightRotationAction);
|
viewer->addAction(rightRotationAction);
|
||||||
viewer->addAction(doublePageAction);
|
viewer->addAction(doublePageAction);
|
||||||
viewer->addAction(doubleMangaPageAction);
|
viewer->addAction(doubleMangaPageAction);
|
||||||
|
viewer->addAction(continuousScrollAction);
|
||||||
YACReader::addSperator(viewer);
|
YACReader::addSperator(viewer);
|
||||||
|
|
||||||
viewer->addAction(showMagnifyingGlassAction);
|
viewer->addAction(showMagnifyingGlassAction);
|
||||||
@ -675,6 +691,7 @@ void MainWindowViewer::createToolBars()
|
|||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
viewMenu->addAction(doublePageAction);
|
viewMenu->addAction(doublePageAction);
|
||||||
viewMenu->addAction(doubleMangaPageAction);
|
viewMenu->addAction(doubleMangaPageAction);
|
||||||
|
viewMenu->addAction(continuousScrollAction);
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
viewMenu->addAction(showMagnifyingGlassAction);
|
viewMenu->addAction(showMagnifyingGlassAction);
|
||||||
|
|
||||||
@ -1247,6 +1264,7 @@ void MainWindowViewer::setUpShortcutsManagement()
|
|||||||
<< rightRotationAction
|
<< rightRotationAction
|
||||||
<< doublePageAction
|
<< doublePageAction
|
||||||
<< doubleMangaPageAction
|
<< doubleMangaPageAction
|
||||||
|
<< continuousScrollAction
|
||||||
<< adjustToFullSizeAction
|
<< adjustToFullSizeAction
|
||||||
<< fitToPageAction
|
<< fitToPageAction
|
||||||
<< increasePageZoomAction
|
<< increasePageZoomAction
|
||||||
@ -1540,6 +1558,7 @@ void MainWindowViewer::setActionsEnabled(bool enabled)
|
|||||||
showMagnifyingGlassAction,
|
showMagnifyingGlassAction,
|
||||||
doublePageAction,
|
doublePageAction,
|
||||||
doubleMangaPageAction,
|
doubleMangaPageAction,
|
||||||
|
continuousScrollAction,
|
||||||
adjustToFullSizeAction,
|
adjustToFullSizeAction,
|
||||||
fitToPageAction,
|
fitToPageAction,
|
||||||
showZoomSliderlAction,
|
showZoomSliderlAction,
|
||||||
@ -1613,6 +1632,7 @@ void MainWindowViewer::applyTheme(const Theme &theme)
|
|||||||
setIcon(showDictionaryAction, toolbarTheme.showDictionaryAction, toolbarTheme.showDictionaryAction18x18);
|
setIcon(showDictionaryAction, toolbarTheme.showDictionaryAction, toolbarTheme.showDictionaryAction18x18);
|
||||||
setIcon(adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction18x18);
|
setIcon(adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction, toolbarTheme.adjustToFullSizeAction18x18);
|
||||||
setIcon(fitToPageAction, toolbarTheme.fitToPageAction, toolbarTheme.fitToPageAction18x18);
|
setIcon(fitToPageAction, toolbarTheme.fitToPageAction, toolbarTheme.fitToPageAction18x18);
|
||||||
|
setIcon(continuousScrollAction, toolbarTheme.continuousScrollAction, toolbarTheme.continuousScrollAction18x18);
|
||||||
setIcon(showFlowAction, toolbarTheme.showFlowAction, toolbarTheme.showFlowAction18x18);
|
setIcon(showFlowAction, toolbarTheme.showFlowAction, toolbarTheme.showFlowAction18x18);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -140,6 +140,7 @@ private:
|
|||||||
QAction *closeAction;
|
QAction *closeAction;
|
||||||
QAction *doublePageAction;
|
QAction *doublePageAction;
|
||||||
QAction *doubleMangaPageAction;
|
QAction *doubleMangaPageAction;
|
||||||
|
QAction *continuousScrollAction;
|
||||||
QAction *showShorcutsAction;
|
QAction *showShorcutsAction;
|
||||||
QAction *showDictionaryAction;
|
QAction *showDictionaryAction;
|
||||||
QAction *adjustToFullSizeAction;
|
QAction *adjustToFullSizeAction;
|
||||||
|
|||||||
@ -546,6 +546,17 @@ QPixmap *Render::getCurrentDoubleMangaPage()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QImage *Render::bufferedImage(int absolutePageIndex) const
|
||||||
|
{
|
||||||
|
int offset = absolutePageIndex - currentIndex;
|
||||||
|
int pos = currentPageBufferedIndex + offset;
|
||||||
|
if (pos < 0 || pos >= buffer.size()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const QImage *img = buffer[pos];
|
||||||
|
return (img && !img->isNull()) ? img : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool Render::currentPageIsDoublePage()
|
bool Render::currentPageIsDoublePage()
|
||||||
{
|
{
|
||||||
if (currentIndex == 0 && Configuration::getConfiguration().getSettings()->value(COVER_IS_SP, true).toBool()) {
|
if (currentIndex == 0 && Configuration::getConfiguration().getSettings()->value(COVER_IS_SP, true).toBool()) {
|
||||||
@ -627,6 +638,8 @@ void Render::setComic(Comic *c)
|
|||||||
|
|
||||||
void Render::prepareAvailablePage(int page)
|
void Render::prepareAvailablePage(int page)
|
||||||
{
|
{
|
||||||
|
emit pageRendered(page);
|
||||||
|
|
||||||
if (!doublePage) {
|
if (!doublePage) {
|
||||||
if (currentIndex == page) {
|
if (currentIndex == page) {
|
||||||
emit currentPageReady();
|
emit currentPageReady();
|
||||||
|
|||||||
@ -142,6 +142,7 @@ public slots:
|
|||||||
bool currentPageIsDoublePage();
|
bool currentPageIsDoublePage();
|
||||||
bool nextPageIsDoublePage();
|
bool nextPageIsDoublePage();
|
||||||
bool previousPageIsDoublePage();
|
bool previousPageIsDoublePage();
|
||||||
|
const QImage *bufferedImage(int absolutePageIndex) const;
|
||||||
void goTo(int index);
|
void goTo(int index);
|
||||||
void doublePageSwitch();
|
void doublePageSwitch();
|
||||||
void setManga(bool manga);
|
void setManga(bool manga);
|
||||||
@ -197,6 +198,7 @@ signals:
|
|||||||
void isLast();
|
void isLast();
|
||||||
void isCover();
|
void isCover();
|
||||||
|
|
||||||
|
void pageRendered(int absolutePageIndex);
|
||||||
void bookmarksUpdated();
|
void bookmarksUpdated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -102,6 +102,8 @@ struct ToolbarTheme {
|
|||||||
QIcon adjustToFullSizeAction18x18;
|
QIcon adjustToFullSizeAction18x18;
|
||||||
QIcon fitToPageAction;
|
QIcon fitToPageAction;
|
||||||
QIcon fitToPageAction18x18;
|
QIcon fitToPageAction18x18;
|
||||||
|
QIcon continuousScrollAction;
|
||||||
|
QIcon continuousScrollAction18x18;
|
||||||
QIcon showFlowAction;
|
QIcon showFlowAction;
|
||||||
QIcon showFlowAction18x18;
|
QIcon showFlowAction18x18;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -139,6 +139,7 @@ Theme makeTheme(const ThemeParams ¶ms)
|
|||||||
setToolbarIconPairT(theme.toolbar.showDictionaryAction, theme.toolbar.showDictionaryAction18x18, ":/images/viewer_toolbar/translator.svg");
|
setToolbarIconPairT(theme.toolbar.showDictionaryAction, theme.toolbar.showDictionaryAction18x18, ":/images/viewer_toolbar/translator.svg");
|
||||||
setToolbarIconPairT(theme.toolbar.adjustToFullSizeAction, theme.toolbar.adjustToFullSizeAction18x18, ":/images/viewer_toolbar/full.svg");
|
setToolbarIconPairT(theme.toolbar.adjustToFullSizeAction, theme.toolbar.adjustToFullSizeAction18x18, ":/images/viewer_toolbar/full.svg");
|
||||||
setToolbarIconPairT(theme.toolbar.fitToPageAction, theme.toolbar.fitToPageAction18x18, ":/images/viewer_toolbar/fitToPage.svg");
|
setToolbarIconPairT(theme.toolbar.fitToPageAction, theme.toolbar.fitToPageAction18x18, ":/images/viewer_toolbar/fitToPage.svg");
|
||||||
|
setToolbarIconPairT(theme.toolbar.continuousScrollAction, theme.toolbar.continuousScrollAction18x18, ":/images/viewer_toolbar/toContinuousScroll.svg");
|
||||||
setToolbarIconPairT(theme.toolbar.showFlowAction, theme.toolbar.showFlowAction18x18, ":/images/viewer_toolbar/flow.svg");
|
setToolbarIconPairT(theme.toolbar.showFlowAction, theme.toolbar.showFlowAction18x18, ":/images/viewer_toolbar/flow.svg");
|
||||||
// end Toolbar & actions
|
// end Toolbar & actions
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "viewer.h"
|
#include "viewer.h"
|
||||||
|
#include "continuous_page_widget.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "magnifying_glass.h"
|
#include "magnifying_glass.h"
|
||||||
#include "goto_flow_widget.h"
|
#include "goto_flow_widget.h"
|
||||||
@ -23,6 +24,7 @@ Viewer::Viewer(QWidget *parent)
|
|||||||
information(false),
|
information(false),
|
||||||
doublePage(false),
|
doublePage(false),
|
||||||
doubleMangaPage(false),
|
doubleMangaPage(false),
|
||||||
|
continuousScroll(false),
|
||||||
zoom(100),
|
zoom(100),
|
||||||
currentPage(nullptr),
|
currentPage(nullptr),
|
||||||
wheelStop(false),
|
wheelStop(false),
|
||||||
@ -40,15 +42,30 @@ Viewer::Viewer(QWidget *parent)
|
|||||||
translatorAnimation->setDuration(150);
|
translatorAnimation->setDuration(150);
|
||||||
translatorXPos = -10000;
|
translatorXPos = -10000;
|
||||||
translator->move(-translator->width(), 10);
|
translator->move(-translator->width(), 10);
|
||||||
// current comic page
|
// current comic page (used in non-continuous mode when a comic is open)
|
||||||
content = new QLabel(this);
|
content = new QLabel(this);
|
||||||
configureContent(tr("Press 'O' to open comic."));
|
content->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||||
|
if (!(devicePixelRatioF() > 1))
|
||||||
|
content->setScaledContents(true);
|
||||||
|
content->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
||||||
|
content->setMouseTracking(true);
|
||||||
|
|
||||||
setWidget(content);
|
// dedicated widget for status messages ("Press 'O' to open comic.", "Loading...", etc.)
|
||||||
|
messageLabel = new QLabel(this);
|
||||||
|
messageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||||
|
messageLabel->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
||||||
|
messageLabel->setText(tr("Press 'O' to open comic."));
|
||||||
|
messageLabel->setFont(QFont("courier new", 12));
|
||||||
|
messageLabel->setMouseTracking(true);
|
||||||
|
|
||||||
|
setWidget(messageLabel);
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setFrameStyle(QFrame::NoFrame);
|
setFrameStyle(QFrame::NoFrame);
|
||||||
setAlignment(Qt::AlignCenter);
|
setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
continuousWidget = new ContinuousPageWidget();
|
||||||
|
continuousWidget->installEventFilter(this);
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
mglass = new MagnifyingGlass(
|
mglass = new MagnifyingGlass(
|
||||||
Configuration::getConfiguration().getMagnifyingGlassSize(),
|
Configuration::getConfiguration().getMagnifyingGlassSize(),
|
||||||
@ -63,7 +80,6 @@ Viewer::Viewer(QWidget *parent)
|
|||||||
});
|
});
|
||||||
|
|
||||||
mglass->hide();
|
mglass->hide();
|
||||||
content->setMouseTracking(true);
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
|
||||||
showCursor();
|
showCursor();
|
||||||
@ -81,6 +97,7 @@ Viewer::Viewer(QWidget *parent)
|
|||||||
bd = new BookmarksDialog(this->parentWidget());
|
bd = new BookmarksDialog(this->parentWidget());
|
||||||
|
|
||||||
render = new Render();
|
render = new Render();
|
||||||
|
continuousWidget->setRender(render);
|
||||||
|
|
||||||
hideCursorTimer = new QTimer();
|
hideCursorTimer = new QTimer();
|
||||||
hideCursorTimer->setSingleShot(true);
|
hideCursorTimer->setSingleShot(true);
|
||||||
@ -91,6 +108,9 @@ Viewer::Viewer(QWidget *parent)
|
|||||||
if (Configuration::getConfiguration().getDoubleMangaPage())
|
if (Configuration::getConfiguration().getDoubleMangaPage())
|
||||||
doubleMangaPageSwitch();
|
doubleMangaPageSwitch();
|
||||||
|
|
||||||
|
if (Configuration::getConfiguration().getContinuousScroll())
|
||||||
|
setContinuousScroll(true);
|
||||||
|
|
||||||
createConnections();
|
createConnections();
|
||||||
|
|
||||||
hideCursorTimer->start(2500);
|
hideCursorTimer->start(2500);
|
||||||
@ -122,7 +142,17 @@ Viewer::~Viewer()
|
|||||||
delete goToFlow;
|
delete goToFlow;
|
||||||
delete translator;
|
delete translator;
|
||||||
delete translatorAnimation;
|
delete translatorAnimation;
|
||||||
delete content;
|
// messageLabel, content or continuousWidget may not be owned by the scroll area
|
||||||
|
// (after takeWidget), so delete whichever ones are not currently set
|
||||||
|
if (widget() != messageLabel) {
|
||||||
|
delete messageLabel;
|
||||||
|
}
|
||||||
|
if (widget() != content) {
|
||||||
|
delete content;
|
||||||
|
}
|
||||||
|
if (widget() != continuousWidget) {
|
||||||
|
delete continuousWidget;
|
||||||
|
}
|
||||||
delete hideCursorTimer;
|
delete hideCursorTimer;
|
||||||
delete informationLabel;
|
delete informationLabel;
|
||||||
delete verticalScroller;
|
delete verticalScroller;
|
||||||
@ -173,9 +203,14 @@ void Viewer::createConnections()
|
|||||||
connect(render, qOverload<unsigned int>(&Render::numPages), this, &Viewer::comicLoaded);
|
connect(render, qOverload<unsigned int>(&Render::numPages), this, &Viewer::comicLoaded);
|
||||||
connect(render, QOverload<int, const QByteArray &>::of(&Render::imageLoaded), goToFlow, &GoToFlowWidget::setImageReady);
|
connect(render, QOverload<int, const QByteArray &>::of(&Render::imageLoaded), goToFlow, &GoToFlowWidget::setImageReady);
|
||||||
connect(render, &Render::currentPageReady, this, &Viewer::updatePage);
|
connect(render, &Render::currentPageReady, this, &Viewer::updatePage);
|
||||||
|
connect(render, &Render::pageRendered, continuousWidget, &ContinuousPageWidget::onPageAvailable);
|
||||||
|
connect(continuousWidget, &ContinuousPageWidget::layoutScrollPositionRequested, this, &Viewer::onContinuousLayoutScrollRequested);
|
||||||
|
connect(render, qOverload<unsigned int>(&Render::numPages), this, &Viewer::onNumPagesReady);
|
||||||
|
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &Viewer::onContinuousScroll);
|
||||||
connect(render, &Render::processingPage, this, &Viewer::setLoadingMessage);
|
connect(render, &Render::processingPage, this, &Viewer::setLoadingMessage);
|
||||||
connect(render, &Render::currentPageIsBookmark, this, &Viewer::pageIsBookmark);
|
connect(render, &Render::currentPageIsBookmark, this, &Viewer::pageIsBookmark);
|
||||||
connect(render, &Render::pageChanged, this, &Viewer::updateInformation);
|
connect(render, &Render::pageChanged, this, &Viewer::updateInformation);
|
||||||
|
connect(render, &Render::pageChanged, this, &Viewer::onRenderPageChanged);
|
||||||
|
|
||||||
connect(render, &Render::isLast, this, &Viewer::showIsLastMessage);
|
connect(render, &Render::isLast, this, &Viewer::showIsLastMessage);
|
||||||
connect(render, &Render::isCover, this, &Viewer::showIsCoverMessage);
|
connect(render, &Render::isCover, this, &Viewer::showIsCoverMessage);
|
||||||
@ -304,11 +339,26 @@ void Viewer::goToLastPage()
|
|||||||
void Viewer::goTo(unsigned int page)
|
void Viewer::goTo(unsigned int page)
|
||||||
{
|
{
|
||||||
direction = 1; // in "go to" direction is always fordward
|
direction = 1; // in "go to" direction is always fordward
|
||||||
|
|
||||||
|
if (continuousScroll) {
|
||||||
|
lastCenterPage = page;
|
||||||
|
continuousWidget->setAnchorPage(page);
|
||||||
|
render->goTo(page);
|
||||||
|
scrollToCurrentContinuousPage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
render->goTo(page);
|
render->goTo(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::updatePage()
|
void Viewer::updatePage()
|
||||||
{
|
{
|
||||||
|
if (continuousScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveWidget(content);
|
||||||
|
|
||||||
QPixmap *previousPage = currentPage;
|
QPixmap *previousPage = currentPage;
|
||||||
if (doublePage) {
|
if (doublePage) {
|
||||||
if (!doubleMangaPage)
|
if (!doubleMangaPage)
|
||||||
@ -397,7 +447,11 @@ void Viewer::increaseZoomFactor()
|
|||||||
{
|
{
|
||||||
zoom = std::min(zoom + 10, 500);
|
zoom = std::min(zoom + 10, 500);
|
||||||
|
|
||||||
updateContentSize();
|
if (continuousScroll) {
|
||||||
|
continuousWidget->setZoomFactor(zoom);
|
||||||
|
} else {
|
||||||
|
updateContentSize();
|
||||||
|
}
|
||||||
notificationsLabel->setText(QString::number(getZoomFactor()) + "%");
|
notificationsLabel->setText(QString::number(getZoomFactor()) + "%");
|
||||||
notificationsLabel->flash();
|
notificationsLabel->flash();
|
||||||
|
|
||||||
@ -407,7 +461,11 @@ void Viewer::decreaseZoomFactor()
|
|||||||
{
|
{
|
||||||
zoom = std::max(zoom - 10, 30);
|
zoom = std::max(zoom - 10, 30);
|
||||||
|
|
||||||
updateContentSize();
|
if (continuousScroll) {
|
||||||
|
continuousWidget->setZoomFactor(zoom);
|
||||||
|
} else {
|
||||||
|
updateContentSize();
|
||||||
|
}
|
||||||
notificationsLabel->setText(QString::number(getZoomFactor()) + "%");
|
notificationsLabel->setText(QString::number(getZoomFactor()) + "%");
|
||||||
notificationsLabel->flash();
|
notificationsLabel->flash();
|
||||||
|
|
||||||
@ -435,16 +493,22 @@ void Viewer::setZoomFactor(int z)
|
|||||||
|
|
||||||
void Viewer::updateVerticalScrollBar()
|
void Viewer::updateVerticalScrollBar()
|
||||||
{
|
{
|
||||||
if (direction > 0)
|
if (direction > 0) {
|
||||||
verticalScrollBar()->setSliderPosition(verticalScrollBar()->minimum());
|
verticalScrollBar()->setSliderPosition(verticalScrollBar()->minimum());
|
||||||
else
|
} else {
|
||||||
verticalScrollBar()->setSliderPosition(verticalScrollBar()->maximum());
|
verticalScrollBar()->setSliderPosition(verticalScrollBar()->maximum());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::scrollDown()
|
void Viewer::scrollDown()
|
||||||
{
|
{
|
||||||
if (verticalScrollBar()->sliderPosition() == verticalScrollBar()->maximum()) {
|
if (verticalScrollBar()->sliderPosition() == verticalScrollBar()->maximum()) {
|
||||||
next();
|
if (continuousScroll) {
|
||||||
|
shouldOpenNext = true;
|
||||||
|
emit openNextComic();
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int currentPos = verticalScrollBar()->sliderPosition();
|
int currentPos = verticalScrollBar()->sliderPosition();
|
||||||
verticalScroller->setDuration(animationDuration());
|
verticalScroller->setDuration(animationDuration());
|
||||||
@ -460,7 +524,12 @@ void Viewer::scrollDown()
|
|||||||
void Viewer::scrollUp()
|
void Viewer::scrollUp()
|
||||||
{
|
{
|
||||||
if (verticalScrollBar()->sliderPosition() == verticalScrollBar()->minimum()) {
|
if (verticalScrollBar()->sliderPosition() == verticalScrollBar()->minimum()) {
|
||||||
prev();
|
if (continuousScroll) {
|
||||||
|
shouldOpenPrevious = true;
|
||||||
|
emit openPreviousComic();
|
||||||
|
} else {
|
||||||
|
prev();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
int currentPos = verticalScrollBar()->sliderPosition();
|
int currentPos = verticalScrollBar()->sliderPosition();
|
||||||
verticalScroller->setDuration(animationDuration());
|
verticalScroller->setDuration(animationDuration());
|
||||||
@ -670,6 +739,11 @@ void Viewer::wheelEventMouse(QWheelEvent *event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (continuousScroll) {
|
||||||
|
animateScroll(*verticalScroller, *verticalScrollBar(), delta.y());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto turnPageOnScroll = !Configuration::getConfiguration().getDoNotTurnPageOnScroll();
|
auto turnPageOnScroll = !Configuration::getConfiguration().getDoNotTurnPageOnScroll();
|
||||||
auto getUseSingleScrollStepToTurnPage = Configuration::getConfiguration().getUseSingleScrollStepToTurnPage();
|
auto getUseSingleScrollStepToTurnPage = Configuration::getConfiguration().getUseSingleScrollStepToTurnPage();
|
||||||
|
|
||||||
@ -718,6 +792,10 @@ void Viewer::wheelEventTrackpad(QWheelEvent *event)
|
|||||||
verticalScrollBar()->setValue(newVerticalValue);
|
verticalScrollBar()->setValue(newVerticalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (continuousScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto turnPageOnScroll = !Configuration::getConfiguration().getDoNotTurnPageOnScroll();
|
auto turnPageOnScroll = !Configuration::getConfiguration().getDoNotTurnPageOnScroll();
|
||||||
auto getUseSingleScrollStepToTurnPage = Configuration::getConfiguration().getUseSingleScrollStepToTurnPage();
|
auto getUseSingleScrollStepToTurnPage = Configuration::getConfiguration().getUseSingleScrollStepToTurnPage();
|
||||||
|
|
||||||
@ -750,11 +828,16 @@ void Viewer::wheelEventTrackpad(QWheelEvent *event)
|
|||||||
|
|
||||||
void Viewer::resizeEvent(QResizeEvent *event)
|
void Viewer::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
|
QScrollArea::resizeEvent(event);
|
||||||
|
|
||||||
|
if (continuousScroll) {
|
||||||
|
continuousWidget->setViewportState(verticalScrollBar()->value(), viewport()->height());
|
||||||
|
}
|
||||||
|
|
||||||
updateContentSize();
|
updateContentSize();
|
||||||
goToFlow->updateSize();
|
goToFlow->updateSize();
|
||||||
goToFlow->move((width() - goToFlow->width()) / 2, height() - goToFlow->height());
|
goToFlow->move((width() - goToFlow->width()) / 2, height() - goToFlow->height());
|
||||||
informationLabel->updatePosition();
|
informationLabel->updatePosition();
|
||||||
QScrollArea::resizeEvent(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap Viewer::pixmap() const
|
QPixmap Viewer::pixmap() const
|
||||||
@ -927,6 +1010,132 @@ void Viewer::doublePageSwitch()
|
|||||||
Configuration::getConfiguration().setDoublePage(doublePage);
|
Configuration::getConfiguration().setDoublePage(doublePage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Viewer::setContinuousScroll(bool enabled)
|
||||||
|
{
|
||||||
|
if (continuousScroll == enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continuousScroll = enabled;
|
||||||
|
Configuration::getConfiguration().setContinuousScroll(continuousScroll);
|
||||||
|
|
||||||
|
if (continuousScroll) {
|
||||||
|
continuousWidget->setZoomFactor(zoom);
|
||||||
|
if (render->hasLoadedComic()) {
|
||||||
|
continuousWidget->setViewportState(verticalScrollBar()->value(), viewport()->height());
|
||||||
|
continuousWidget->setNumPages(render->numPages());
|
||||||
|
// set the current page as model state before any layout/scroll happens
|
||||||
|
lastCenterPage = render->getIndex();
|
||||||
|
continuousWidget->setAnchorPage(lastCenterPage);
|
||||||
|
// pick up sizes of pages already in the buffer
|
||||||
|
continuousWidget->probeBufferedPages();
|
||||||
|
// trigger a render cycle so new pages arrive via pageRendered signal
|
||||||
|
render->update();
|
||||||
|
setActiveWidget(continuousWidget);
|
||||||
|
scrollToCurrentContinuousPage();
|
||||||
|
continuousWidget->update();
|
||||||
|
viewport()->update();
|
||||||
|
}
|
||||||
|
// if no comic is loaded, messageLabel stays as the active widget
|
||||||
|
} else {
|
||||||
|
lastCenterPage = -1;
|
||||||
|
if (render->hasLoadedComic()) {
|
||||||
|
updatePage();
|
||||||
|
}
|
||||||
|
// if no comic is loaded, messageLabel stays as the active widget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::onContinuousScroll(int value)
|
||||||
|
{
|
||||||
|
if (!continuousScroll || !render->hasLoadedComic()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
continuousWidget->setViewportState(value, viewport()->height());
|
||||||
|
|
||||||
|
int center = continuousWidget->centerPage(value, viewport()->height());
|
||||||
|
|
||||||
|
if (center != lastCenterPage && center >= 0) {
|
||||||
|
lastCenterPage = center;
|
||||||
|
continuousWidget->setAnchorPage(center);
|
||||||
|
syncingRenderFromContinuousScroll = true;
|
||||||
|
render->goTo(center);
|
||||||
|
syncingRenderFromContinuousScroll = false;
|
||||||
|
emit pageAvailable(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::onContinuousLayoutScrollRequested(int scrollY)
|
||||||
|
{
|
||||||
|
if (!continuousScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *sb = verticalScrollBar();
|
||||||
|
const int target = qBound(sb->minimum(), scrollY, sb->maximum());
|
||||||
|
|
||||||
|
sb->blockSignals(true);
|
||||||
|
sb->setValue(target);
|
||||||
|
sb->blockSignals(false);
|
||||||
|
|
||||||
|
continuousWidget->setViewportState(target, viewport()->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::scrollToCurrentContinuousPage()
|
||||||
|
{
|
||||||
|
if (lastCenterPage < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto applyPosition = [this]() {
|
||||||
|
auto *sb = verticalScrollBar();
|
||||||
|
int targetY = continuousWidget->yPositionForPage(lastCenterPage);
|
||||||
|
targetY = qBound(sb->minimum(), targetY, sb->maximum());
|
||||||
|
|
||||||
|
sb->blockSignals(true);
|
||||||
|
sb->setValue(targetY);
|
||||||
|
sb->blockSignals(false);
|
||||||
|
|
||||||
|
continuousWidget->setViewportState(targetY, viewport()->height());
|
||||||
|
|
||||||
|
continuousWidget->update();
|
||||||
|
viewport()->update();
|
||||||
|
};
|
||||||
|
|
||||||
|
applyPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::onNumPagesReady(unsigned int numPages)
|
||||||
|
{
|
||||||
|
if (continuousScroll && numPages > 0) {
|
||||||
|
setActiveWidget(continuousWidget);
|
||||||
|
|
||||||
|
continuousWidget->setViewportState(verticalScrollBar()->value(), viewport()->height());
|
||||||
|
continuousWidget->setNumPages(numPages);
|
||||||
|
|
||||||
|
int page = lastCenterPage;
|
||||||
|
if (page < 0) {
|
||||||
|
page = render->getIndex();
|
||||||
|
}
|
||||||
|
page = qBound(0, page, static_cast<int>(numPages) - 1);
|
||||||
|
lastCenterPage = page;
|
||||||
|
continuousWidget->setAnchorPage(page);
|
||||||
|
|
||||||
|
scrollToCurrentContinuousPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::onRenderPageChanged(int page)
|
||||||
|
{
|
||||||
|
if (!continuousScroll || page < 0 || page == lastCenterPage || syncingRenderFromContinuousScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCenterPage = page;
|
||||||
|
continuousWidget->setAnchorPage(page);
|
||||||
|
scrollToCurrentContinuousPage();
|
||||||
|
}
|
||||||
|
|
||||||
void Viewer::setMangaWithoutStoringSetting(bool manga)
|
void Viewer::setMangaWithoutStoringSetting(bool manga)
|
||||||
{
|
{
|
||||||
doubleMangaPage = manga;
|
doubleMangaPage = manga;
|
||||||
@ -948,6 +1157,8 @@ void Viewer::resetContent()
|
|||||||
{
|
{
|
||||||
configureContent(tr("Press 'O' to open comic."));
|
configureContent(tr("Press 'O' to open comic."));
|
||||||
goToFlow->reset();
|
goToFlow->reset();
|
||||||
|
continuousWidget->reset();
|
||||||
|
lastCenterPage = -1;
|
||||||
emit reset();
|
emit reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,7 +1169,9 @@ void Viewer::setLoadingMessage()
|
|||||||
restoreMagnifyingGlass = true;
|
restoreMagnifyingGlass = true;
|
||||||
}
|
}
|
||||||
emit pageAvailable(false);
|
emit pageAvailable(false);
|
||||||
configureContent(tr("Loading...please wait!"));
|
if (!continuousScroll) {
|
||||||
|
configureContent(tr("Loading...please wait!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::setPageUnavailableMessage()
|
void Viewer::setPageUnavailableMessage()
|
||||||
@ -973,15 +1186,9 @@ void Viewer::setPageUnavailableMessage()
|
|||||||
|
|
||||||
void Viewer::configureContent(QString msg)
|
void Viewer::configureContent(QString msg)
|
||||||
{
|
{
|
||||||
content->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
messageLabel->setText(msg);
|
||||||
if (!(devicePixelRatioF() > 1))
|
setActiveWidget(messageLabel);
|
||||||
content->setScaledContents(true);
|
|
||||||
content->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
|
|
||||||
content->setText(msg);
|
|
||||||
content->setFont(QFont("courier new", 12));
|
|
||||||
content->adjustSize();
|
|
||||||
setFocus(Qt::ShortcutFocusReason);
|
setFocus(Qt::ShortcutFocusReason);
|
||||||
// emit showingText();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::hideCursor()
|
void Viewer::hideCursor()
|
||||||
@ -1022,7 +1229,8 @@ void Viewer::applyTheme(const Theme &theme)
|
|||||||
updateBackgroundColor(Configuration::getConfiguration().getBackgroundColor(viewerTheme.defaultBackgroundColor));
|
updateBackgroundColor(Configuration::getConfiguration().getBackgroundColor(viewerTheme.defaultBackgroundColor));
|
||||||
|
|
||||||
const QString textColor = viewerTheme.defaultTextColor.name(QColor::HexArgb);
|
const QString textColor = viewerTheme.defaultTextColor.name(QColor::HexArgb);
|
||||||
content->setStyleSheet(QStringLiteral("QLabel { color : %1; background: transparent; }").arg(textColor));
|
messageLabel->setStyleSheet(QStringLiteral("QLabel { color : %1; background: transparent; }").arg(textColor));
|
||||||
|
content->setStyleSheet(QStringLiteral("QLabel { background: transparent; }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::animateShowTranslator()
|
void Viewer::animateShowTranslator()
|
||||||
@ -1072,10 +1280,46 @@ void Viewer::mouseMoveEvent(QMouseEvent *event)
|
|||||||
mouseHandler->mouseMoveEvent(event);
|
mouseHandler->mouseMoveEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Viewer::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (obj == continuousWidget && event->type() == QEvent::MouseMove) {
|
||||||
|
auto *mouseEvent = static_cast<QMouseEvent *>(event);
|
||||||
|
// Map position from continuousWidget coords to Viewer coords so the
|
||||||
|
// go-to-flow proximity check and cursor management work correctly.
|
||||||
|
QPointF viewerPos = mapFromGlobal(mouseEvent->globalPosition().toPoint());
|
||||||
|
QMouseEvent mappedEvent(mouseEvent->type(),
|
||||||
|
viewerPos,
|
||||||
|
mouseEvent->globalPosition(),
|
||||||
|
mouseEvent->button(),
|
||||||
|
mouseEvent->buttons(),
|
||||||
|
mouseEvent->modifiers());
|
||||||
|
mouseHandler->mouseMoveEvent(&mappedEvent);
|
||||||
|
}
|
||||||
|
return QScrollArea::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::setActiveWidget(QWidget *w)
|
||||||
|
{
|
||||||
|
if (widget() == w) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verticalScrollBar()->blockSignals(true);
|
||||||
|
takeWidget();
|
||||||
|
const bool isContinuous = (w == continuousWidget);
|
||||||
|
setWidgetResizable(isContinuous);
|
||||||
|
setVerticalScrollBarPolicy(isContinuous ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
|
||||||
|
setWidget(w);
|
||||||
|
verticalScrollBar()->blockSignals(false);
|
||||||
|
}
|
||||||
|
|
||||||
void Viewer::updateZoomRatio(int ratio)
|
void Viewer::updateZoomRatio(int ratio)
|
||||||
{
|
{
|
||||||
zoom = ratio;
|
zoom = ratio;
|
||||||
updateContentSize();
|
if (continuousScroll) {
|
||||||
|
continuousWidget->setZoomFactor(zoom);
|
||||||
|
} else {
|
||||||
|
updateContentSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Viewer::getIsMangaMode()
|
bool Viewer::getIsMangaMode()
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class GoToDialog;
|
|||||||
class YACReaderTranslator;
|
class YACReaderTranslator;
|
||||||
class GoToFlowWidget;
|
class GoToFlowWidget;
|
||||||
class Bookmarks;
|
class Bookmarks;
|
||||||
|
class ContinuousPageWidget;
|
||||||
class PageLabelWidget;
|
class PageLabelWidget;
|
||||||
class NotificationsLabelWidget;
|
class NotificationsLabelWidget;
|
||||||
|
|
||||||
@ -113,11 +114,13 @@ public slots:
|
|||||||
int getCurrentPageNumber();
|
int getCurrentPageNumber();
|
||||||
void updateZoomRatio(int ratio);
|
void updateZoomRatio(int ratio);
|
||||||
bool getIsMangaMode();
|
bool getIsMangaMode();
|
||||||
|
void setContinuousScroll(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool information;
|
bool information;
|
||||||
bool doublePage;
|
bool doublePage;
|
||||||
bool doubleMangaPage;
|
bool doubleMangaPage;
|
||||||
|
bool continuousScroll;
|
||||||
|
|
||||||
int zoom;
|
int zoom;
|
||||||
|
|
||||||
@ -144,6 +147,10 @@ private:
|
|||||||
|
|
||||||
//! Widgets
|
//! Widgets
|
||||||
QLabel *content;
|
QLabel *content;
|
||||||
|
QLabel *messageLabel;
|
||||||
|
ContinuousPageWidget *continuousWidget;
|
||||||
|
int lastCenterPage = -1;
|
||||||
|
bool syncingRenderFromContinuousScroll = false;
|
||||||
|
|
||||||
YACReaderTranslator *translator;
|
YACReaderTranslator *translator;
|
||||||
int translatorXPos;
|
int translatorXPos;
|
||||||
@ -183,12 +190,19 @@ private:
|
|||||||
// Zero when animations are disabled
|
// Zero when animations are disabled
|
||||||
int animationDuration() const;
|
int animationDuration() const;
|
||||||
void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta);
|
void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta);
|
||||||
|
void onContinuousScroll(int value);
|
||||||
|
void onContinuousLayoutScrollRequested(int scrollY);
|
||||||
|
void scrollToCurrentContinuousPage();
|
||||||
|
void onNumPagesReady(unsigned int numPages);
|
||||||
|
void onRenderPageChanged(int page);
|
||||||
|
void setActiveWidget(QWidget *w);
|
||||||
|
|
||||||
//! Mouse handler
|
//! Mouse handler
|
||||||
std::unique_ptr<YACReader::MouseHandler> mouseHandler;
|
std::unique_ptr<YACReader::MouseHandler> mouseHandler;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void applyTheme(const Theme &theme) override;
|
void applyTheme(const Theme &theme) override;
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Viewer(QWidget *parent = nullptr);
|
Viewer(QWidget *parent = nullptr);
|
||||||
|
|||||||
@ -55,6 +55,7 @@
|
|||||||
<file>../images/viewer_toolbar/shortcuts.svg</file>
|
<file>../images/viewer_toolbar/shortcuts.svg</file>
|
||||||
<file>../images/viewer_toolbar/showBookmarks.svg</file>
|
<file>../images/viewer_toolbar/showBookmarks.svg</file>
|
||||||
<file>../images/viewer_toolbar/toHeight.svg</file>
|
<file>../images/viewer_toolbar/toHeight.svg</file>
|
||||||
|
<file>../images/viewer_toolbar/toContinuousScroll.svg</file>
|
||||||
<file>../images/viewer_toolbar/toWidth.svg</file>
|
<file>../images/viewer_toolbar/toWidth.svg</file>
|
||||||
<file>../images/viewer_toolbar/translator.svg</file>
|
<file>../images/viewer_toolbar/translator.svg</file>
|
||||||
<file>../images/viewer_toolbar/zoom.svg</file>
|
<file>../images/viewer_toolbar/zoom.svg</file>
|
||||||
@ -82,6 +83,7 @@
|
|||||||
<file>../images/viewer_toolbar/shortcuts_18x18.svg</file>
|
<file>../images/viewer_toolbar/shortcuts_18x18.svg</file>
|
||||||
<file>../images/viewer_toolbar/showBookmarks_18x18.svg</file>
|
<file>../images/viewer_toolbar/showBookmarks_18x18.svg</file>
|
||||||
<file>../images/viewer_toolbar/toHeight_18x18.svg</file>
|
<file>../images/viewer_toolbar/toHeight_18x18.svg</file>
|
||||||
|
<file>../images/viewer_toolbar/toContinuousScroll_18x18.svg</file>
|
||||||
<file>../images/viewer_toolbar/toWidth_18x18.svg</file>
|
<file>../images/viewer_toolbar/toWidth_18x18.svg</file>
|
||||||
<file>../images/viewer_toolbar/translator_18x18.svg</file>
|
<file>../images/viewer_toolbar/translator_18x18.svg</file>
|
||||||
<file>../images/viewer_toolbar/zoom_18x18.svg</file>
|
<file>../images/viewer_toolbar/zoom_18x18.svg</file>
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
#define START_TO_TRAY "START_TO_TRAY"
|
#define START_TO_TRAY "START_TO_TRAY"
|
||||||
#define DOUBLE_PAGE "DOUBLE_PAGE"
|
#define DOUBLE_PAGE "DOUBLE_PAGE"
|
||||||
#define DOUBLE_MANGA_PAGE "DOUBLE_MANGA_PAGE"
|
#define DOUBLE_MANGA_PAGE "DOUBLE_MANGA_PAGE"
|
||||||
|
#define CONTINUOUS_SCROLL "CONTINUOUS_SCROLL"
|
||||||
#define COVER_IS_SP "COVER_IS_SP"
|
#define COVER_IS_SP "COVER_IS_SP"
|
||||||
#define BACKGROUND_COLOR "BACKGROUND_COLOR_10"
|
#define BACKGROUND_COLOR "BACKGROUND_COLOR_10"
|
||||||
#define SHOW_TOOLBARS "SHOW_TOOLBARS"
|
#define SHOW_TOOLBARS "SHOW_TOOLBARS"
|
||||||
|
|||||||
13
images/viewer_toolbar/toContinuousScroll.svg
Normal file
13
images/viewer_toolbar/toContinuousScroll.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #f0f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-1" d="M2,2h12v1c0,.55-.45,1-1,1H3c-.55,0-1-.45-1-1v-1h0Z"/>
|
||||||
|
<path class="cls-1" d="M3,12h10c.55,0,1,.45,1,1v1H2v-1c0-.55.45-1,1-1Z"/>
|
||||||
|
<rect class="cls-1" x="2" y="5" width="12" height="6" rx="1" ry="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
13
images/viewer_toolbar/toContinuousScroll_18x18.svg
Normal file
13
images/viewer_toolbar/toContinuousScroll_18x18.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #f0f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-1" d="M3,3h12v1c0,.55-.45,1-1,1H4c-.55,0-1-.45-1-1v-1h0Z"/>
|
||||||
|
<path class="cls-1" d="M4,13h10c.55,0,1,.45,1,1v1H3v-1c0-.55.45-1,1-1Z"/>
|
||||||
|
<rect class="cls-1" x="3" y="6" width="12" height="6" rx="1" ry="1"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 453 B |
Reference in New Issue
Block a user