Skip to content

Commit f8d3dcc

Browse files
committed
feat: auto long image mode
1 parent f0ed9d0 commit f8d3dcc

File tree

8 files changed

+136
-8
lines changed

8 files changed

+136
-8
lines changed

app/graphicsview.cpp

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
1+
// SPDX-FileCopyrightText: 2025 Gary Wang <git@blumia.net>
22
//
33
// SPDX-License-Identifier: MIT
44

@@ -124,6 +124,7 @@ void GraphicsView::resetTransform()
124124
void GraphicsView::zoomView(qreal scaleFactor)
125125
{
126126
m_enableFitInView = false;
127+
m_longImageMode = false;
127128
scale(scaleFactor, scaleFactor);
128129
applyTransformationModeByScaleFactor();
129130
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
@@ -140,6 +141,10 @@ void GraphicsView::rotateView(bool clockwise)
140141
0, 0, 1);
141142
tf = transform() * tf;
142143
setTransform(tf);
144+
145+
// Apply transformation mode but don't emit navigator signal here
146+
// Let displayScene() handle the navigator visibility correctly
147+
applyTransformationModeByScaleFactor();
143148
}
144149

145150
void GraphicsView::flipView(bool horizontal)
@@ -261,15 +266,93 @@ void GraphicsView::fitByOrientation(Qt::Orientation ori, bool scaleDownOnly)
261266
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
262267
}
263268

269+
bool GraphicsView::isLongImage() const
270+
{
271+
// Get the transformed image size (considering rotation and other transforms)
272+
QRectF transformedRect = transform().mapRect(sceneRect());
273+
QSizeF imageSize = transformedRect.size();
274+
275+
if (imageSize.isEmpty()) return false;
276+
277+
qreal aspectRatio = imageSize.width() / imageSize.height();
278+
279+
// Check if aspect ratio exceeds 5:2 (wide) or 2:5 (tall)
280+
return aspectRatio > 2.5 || aspectRatio < 0.4;
281+
}
282+
283+
bool GraphicsView::shouldEnterLongImageMode() const
284+
{
285+
// Check if long image mode is enabled in settings
286+
if (!Settings::instance()->autoLongImageMode()) return false;
287+
288+
// Check if image is a long image
289+
if (!isLongImage()) return false;
290+
291+
// Check if transformed image size is larger than the size of the view
292+
QSizeF imageSize = transform().mapRect(sceneRect()).size();
293+
QSizeF viewSize = viewport()->size();
294+
295+
return imageSize.width() > viewSize.width() || imageSize.height() > viewSize.height();
296+
}
297+
298+
void GraphicsView::applyLongImageMode()
299+
{
300+
if (!shouldEnterLongImageMode()) {
301+
m_longImageMode = false;
302+
return;
303+
}
304+
305+
m_longImageMode = true;
306+
applyLongImageModeDirect();
307+
}
308+
309+
void GraphicsView::applyLongImageModeDirect()
310+
{
311+
// Determine image orientation based on current transform
312+
QRectF transformedRect = transform().mapRect(sceneRect());
313+
qreal aspectRatio = transformedRect.width() / transformedRect.height();
314+
bool isTallImage = aspectRatio < 0.4;
315+
bool isWideImage = aspectRatio > 2.5;
316+
317+
// Use fitByOrientation with the migrated logic
318+
if (isTallImage) {
319+
// Tall image (height >> width): fit by width
320+
fitByOrientation(Qt::Horizontal, true); // scaleDownOnly = true
321+
} else if (isWideImage) {
322+
// Wide image (width >> height): fit by height
323+
fitByOrientation(Qt::Vertical, true); // scaleDownOnly = true
324+
}
325+
}
326+
327+
bool GraphicsView::isInLongImageMode() const
328+
{
329+
return m_longImageMode;
330+
}
331+
264332
void GraphicsView::displayScene()
265333
{
266334
if (shouldAvoidTransform()) {
267335
emit navigatorViewRequired(!isThingSmallerThanWindowWith(transform()), transform());
268336
return;
269337
}
270338

339+
// Check if should apply long image mode
340+
if (shouldEnterLongImageMode()) {
341+
applyLongImageMode();
342+
m_firstUserMediaLoaded = true;
343+
return;
344+
}
345+
346+
// Not in long image mode
347+
m_longImageMode = false;
348+
271349
if (isSceneBiggerThanView()) {
272350
fitInView(sceneRect(), Qt::KeepAspectRatio);
351+
// After fitInView, the image should fit the window, so hide navigator
352+
emit navigatorViewRequired(false, transform());
353+
} else {
354+
// Image is already smaller than window, no navigator needed
355+
emit navigatorViewRequired(false, transform());
273356
}
274357

275358
m_enableFitInView = true;
@@ -291,6 +374,11 @@ void GraphicsView::setEnableAutoFitInView(bool enable)
291374
m_enableFitInView = enable;
292375
}
293376

377+
void GraphicsView::setLongImageMode(bool enable)
378+
{
379+
m_longImageMode = enable;
380+
}
381+
294382
bool GraphicsView::avoidResetTransform() const
295383
{
296384
return m_avoidResetTransform;
@@ -363,7 +451,12 @@ void GraphicsView::wheelEvent(QWheelEvent *event)
363451

364452
void GraphicsView::resizeEvent(QResizeEvent *event)
365453
{
366-
if (m_enableFitInView) {
454+
if (m_longImageMode) {
455+
// In long image mode, reapply long image logic on resize
456+
// We directly apply the long image mode logic without rechecking
457+
// if we should enter long image mode, as the mode is already active
458+
applyLongImageModeDirect();
459+
} else if (m_enableFitInView) {
367460
bool originalSizeSmallerThanWindow = isThingSmallerThanWindowWith(resetScale(transform()));
368461
if (originalSizeSmallerThanWindow && scaleFactor() >= 1) {
369462
// no longer need to do fitInView()

app/graphicsview.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,20 @@ class GraphicsView : public QGraphicsView
3939
void displayScene();
4040
bool isSceneBiggerThanView() const;
4141
void setEnableAutoFitInView(bool enable = true);
42+
void setLongImageMode(bool enable = true);
4243

4344
bool avoidResetTransform() const;
4445
void setAvoidResetTransform(bool avoidReset);
4546

4647
static QTransform resetScale(const QTransform & orig);
4748

49+
// Long image mode support
50+
bool isLongImage() const;
51+
bool shouldEnterLongImageMode() const;
52+
void applyLongImageMode();
53+
void applyLongImageModeDirect();
54+
bool isInLongImageMode() const;
55+
4856
signals:
4957
void navigatorViewRequired(bool required, QTransform transform);
5058
void viewportRectChanged();
@@ -70,6 +78,7 @@ public slots:
7078
// ... or even more? e.g. "fit/snap width" things...
7179
// Currently it's "no fit" when it's false and "fit when view is smaller" when it's true.
7280
bool m_enableFitInView = false;
81+
bool m_longImageMode = false;
7382
bool m_avoidResetTransform = false;
7483
bool m_checkerboardEnabled = false;
7584
bool m_useLightCheckerboard = false;

app/mainwindow.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ void MainWindow::on_actionActualSize_triggered()
700700
{
701701
m_graphicsView->resetScale();
702702
m_graphicsView->setEnableAutoFitInView(false);
703+
m_graphicsView->setLongImageMode(false);
703704
}
704705

705706
void MainWindow::on_actionToggleMaximize_triggered()
@@ -728,6 +729,7 @@ void MainWindow::on_actionFitInView_triggered()
728729
{
729730
m_graphicsView->fitInView(m_gv->sceneRect(), Qt::KeepAspectRatio);
730731
m_graphicsView->setEnableAutoFitInView(m_graphicsView->scaleFactor() <= 1);
732+
m_graphicsView->setLongImageMode(false);
731733
}
732734

733735
void MainWindow::on_actionFitByWidth_triggered()
@@ -813,14 +815,12 @@ void MainWindow::on_actionRotateClockwise_triggered()
813815
{
814816
m_graphicsView->rotateView();
815817
m_graphicsView->displayScene();
816-
m_gv->setVisible(false);
817818
}
818819

819820
void MainWindow::on_actionRotateCounterClockwise_triggered()
820821
{
821822
m_graphicsView->rotateView(false);
822823
m_graphicsView->displayScene();
823-
m_gv->setVisible(false);
824824
}
825825

826826
void MainWindow::on_actionPrevPicture_triggered()

app/navigatorview.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <QMouseEvent>
1111
#include <QDebug>
12+
#include <QTimer>
1213

1314
NavigatorView::NavigatorView(QWidget *parent)
1415
: QGraphicsView (parent)
@@ -34,10 +35,14 @@ void NavigatorView::setOpacity(qreal opacity, bool animated)
3435

3536
void NavigatorView::updateMainViewportRegion()
3637
{
37-
if (m_mainView != nullptr) {
38-
m_viewportRegion = mapFromScene(m_mainView->mapToScene(m_mainView->rect()));
39-
update();
40-
}
38+
// Use QTimer::singleShot with lambda to delay the update
39+
// This ensures all geometry updates are complete before calculating viewport region
40+
QTimer::singleShot(0, [this]() {
41+
if (m_mainView != nullptr) {
42+
m_viewportRegion = mapFromScene(m_mainView->mapToScene(m_mainView->rect()));
43+
update();
44+
}
45+
});
4146
}
4247

4348
void NavigatorView::mousePressEvent(QMouseEvent *event)

app/settings.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ bool Settings::loopGallery() const
6565
return m_qsettings->value("loop_gallery", true).toBool();
6666
}
6767

68+
bool Settings::autoLongImageMode() const
69+
{
70+
return m_qsettings->value("auto_long_image_mode", true).toBool();
71+
}
72+
6873
Settings::DoubleClickBehavior Settings::doubleClickBehavior() const
6974
{
7075
QString result = m_qsettings->value("double_click_behavior", "Close").toString();
@@ -117,6 +122,12 @@ void Settings::setLoopGallery(bool on)
117122
m_qsettings->sync();
118123
}
119124

125+
void Settings::setAutoLongImageMode(bool on)
126+
{
127+
m_qsettings->setValue("auto_long_image_mode", on);
128+
m_qsettings->sync();
129+
}
130+
120131
void Settings::setDoubleClickBehavior(DoubleClickBehavior dcb)
121132
{
122133
m_qsettings->setValue("double_click_behavior", QEnumHelper::toString(dcb));

app/settings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class Settings : public QObject
3838
bool useBuiltInCloseAnimation() const;
3939
bool useLightCheckerboard() const;
4040
bool loopGallery() const;
41+
bool autoLongImageMode() const;
4142
DoubleClickBehavior doubleClickBehavior() const;
4243
MouseWheelBehavior mouseWheelBehavior() const;
4344
WindowSizeBehavior initWindowSizeBehavior() const;
@@ -47,6 +48,7 @@ class Settings : public QObject
4748
void setUseBuiltInCloseAnimation(bool on);
4849
void setUseLightCheckerboard(bool light);
4950
void setLoopGallery(bool on);
51+
void setAutoLongImageMode(bool on);
5052
void setDoubleClickBehavior(DoubleClickBehavior dcb);
5153
void setMouseWheelBehavior(MouseWheelBehavior mwb);
5254
void setInitWindowSizeBehavior(WindowSizeBehavior wsb);

app/settingsdialog.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
2323
, m_useBuiltInCloseAnimation(new QCheckBox)
2424
, m_useLightCheckerboard(new QCheckBox)
2525
, m_loopGallery(new QCheckBox)
26+
, m_autoLongImageMode(new QCheckBox)
2627
, m_doubleClickBehavior(new QComboBox)
2728
, m_mouseWheelBehavior(new QComboBox)
2829
, m_initWindowSizeBehavior(new QComboBox)
@@ -123,6 +124,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
123124
settingsForm->addRow(tr("Use built-in close window animation"), m_useBuiltInCloseAnimation);
124125
settingsForm->addRow(tr("Use light-color checkerboard"), m_useLightCheckerboard);
125126
settingsForm->addRow(tr("Loop the loaded gallery"), m_loopGallery);
127+
settingsForm->addRow(tr("Auto long image mode"), m_autoLongImageMode);
126128
settingsForm->addRow(tr("Double-click behavior"), m_doubleClickBehavior);
127129
settingsForm->addRow(tr("Mouse wheel behavior"), m_mouseWheelBehavior);
128130
settingsForm->addRow(tr("Default window size"), m_initWindowSizeBehavior);
@@ -132,6 +134,7 @@ SettingsDialog::SettingsDialog(QWidget *parent)
132134
m_useBuiltInCloseAnimation->setChecked(Settings::instance()->useBuiltInCloseAnimation());
133135
m_useLightCheckerboard->setChecked(Settings::instance()->useLightCheckerboard());
134136
m_loopGallery->setChecked(Settings::instance()->loopGallery());
137+
m_autoLongImageMode->setChecked(Settings::instance()->autoLongImageMode());
135138
m_doubleClickBehavior->setModel(new QStringListModel(dcbDropDown));
136139
Settings::DoubleClickBehavior dcb = Settings::instance()->doubleClickBehavior();
137140
m_doubleClickBehavior->setCurrentIndex(static_cast<int>(dcb));
@@ -174,6 +177,10 @@ SettingsDialog::SettingsDialog(QWidget *parent)
174177
Settings::instance()->setLoopGallery(state == Qt::Checked);
175178
});
176179

180+
connect(m_autoLongImageMode, &QCHECKBOX_CHECKSTATECHANGED, this, [ = ](QT_CHECKSTATE state){
181+
Settings::instance()->setAutoLongImageMode(state == Qt::Checked);
182+
});
183+
177184
connect(m_doubleClickBehavior, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [ = ](int index){
178185
Settings::instance()->setDoubleClickBehavior(_dc_options.at(index).first);
179186
});

app/settingsdialog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public slots:
2626
QCheckBox * m_useBuiltInCloseAnimation = nullptr;
2727
QCheckBox * m_useLightCheckerboard = nullptr;
2828
QCheckBox * m_loopGallery = nullptr;
29+
QCheckBox * m_autoLongImageMode = nullptr;
2930
QComboBox * m_doubleClickBehavior = nullptr;
3031
QComboBox * m_mouseWheelBehavior = nullptr;
3132
QComboBox * m_initWindowSizeBehavior = nullptr;

0 commit comments

Comments
 (0)