summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--desktop-widgets/divepicturewidget.cpp14
-rw-r--r--desktop-widgets/divepicturewidget.h2
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.cpp9
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.h1
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.ui36
-rw-r--r--profile-widget/profilewidget2.cpp2
-rw-r--r--qt-models/divepicturemodel.cpp68
-rw-r--r--qt-models/divepicturemodel.h5
8 files changed, 120 insertions, 17 deletions
diff --git a/desktop-widgets/divepicturewidget.cpp b/desktop-widgets/divepicturewidget.cpp
index a6864a5d9..46de16d09 100644
--- a/desktop-widgets/divepicturewidget.cpp
+++ b/desktop-widgets/divepicturewidget.cpp
@@ -59,3 +59,17 @@ void DivePictureWidget::mousePressEvent(QMouseEvent *event)
QListView::mousePressEvent(event);
}
}
+
+void DivePictureWidget::wheelEvent(QWheelEvent *event)
+{
+ if (event->modifiers() == Qt::ControlModifier) {
+ // Angle delta is given in eighth parts of a degree. A classical mouse
+ // wheel click is 15 degrees. Each click should correspond to one zoom step.
+ // Therefore, divide by 15*8=120. To also support touch pads and finer-grained
+ // mouse wheels, take care to always round away from zero.
+ int delta = event->angleDelta().y();
+ int carry = delta > 0 ? 119 : -119;
+ emit zoomLevelChanged((delta + carry) / 120);
+ } else
+ QListView::wheelEvent(event);
+}
diff --git a/desktop-widgets/divepicturewidget.h b/desktop-widgets/divepicturewidget.h
index 09d4608c4..bf48a9d1b 100644
--- a/desktop-widgets/divepicturewidget.h
+++ b/desktop-widgets/divepicturewidget.h
@@ -14,9 +14,11 @@ public:
protected:
void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+ void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
signals:
void photoDoubleClicked(const QString filePath);
+ void zoomLevelChanged(int delta);
};
class DivePictureThumbnailThread : public QThread {
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.cpp b/desktop-widgets/tab-widgets/TabDivePhotos.cpp
index 86749e770..571e0a3ac 100644
--- a/desktop-widgets/tab-widgets/TabDivePhotos.cpp
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.cpp
@@ -29,6 +29,10 @@ TabDivePhotos::TabDivePhotos(QWidget *parent)
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
);
+ connect(ui->photosView, &DivePictureWidget::zoomLevelChanged,
+ this, &TabDivePhotos::changeZoomLevel);
+ connect(ui->zoomSlider, &QAbstractSlider::valueChanged,
+ DivePictureModel::instance(), &DivePictureModel::setZoomLevel);
}
TabDivePhotos::~TabDivePhotos()
@@ -98,3 +102,8 @@ void TabDivePhotos::updateData()
divePictureModel->updateDivePictures();
}
+void TabDivePhotos::changeZoomLevel(int delta)
+{
+ // We count on QSlider doing bound checks
+ ui->zoomSlider->setValue(ui->zoomSlider->value() + delta);
+}
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.h b/desktop-widgets/tab-widgets/TabDivePhotos.h
index 9b711595c..e2e7aa05c 100644
--- a/desktop-widgets/tab-widgets/TabDivePhotos.h
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.h
@@ -26,6 +26,7 @@ private slots:
void addPhotosFromURL();
void removeAllPhotos();
void removeSelectedPhotos();
+ void changeZoomLevel(int delta);
private:
Ui::TabDivePhotos *ui;
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.ui b/desktop-widgets/tab-widgets/TabDivePhotos.ui
index 35cfd375a..21e4544e6 100644
--- a/desktop-widgets/tab-widgets/TabDivePhotos.ui
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.ui
@@ -21,6 +21,42 @@
</property>
</widget>
</item>
+ <item>
+ <layout class="QHBoxLayout" name="zoomLayout">
+ <item>
+ <widget class="QLabel" name="zoomLabel">
+ <property name="text">
+ <string>Zoom level</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSlider" name="zoomSlider">
+ <property name="minimum">
+ <number>-10</number>
+ </property>
+ <property name="maximum">
+ <number>10</number>
+ </property>
+ <property name="singleStep">
+ <number>1</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBelow</enum>
+ </property>
+ <property name="tickInterval">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
<customwidgets>
diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp
index b7b6057fe..55eb40000 100644
--- a/profile-widget/profilewidget2.cpp
+++ b/profile-widget/profilewidget2.cpp
@@ -2007,7 +2007,7 @@ void ProfileWidget2::plotPictures()
if (!offsetSeconds)
continue;
DivePictureItem *item = new DivePictureItem();
- item->setPixmap(m->index(i, 0).data(Qt::DecorationRole).value<QPixmap>());
+ item->setPixmap(m->index(i, 0).data(Qt::UserRole).value<QPixmap>());
item->setFileUrl(m->index(i, 1).data().toString());
// let's put the picture at the correct time, but at a fixed "depth" on the profile
// not sure this is ideal, but it seems to look right.
diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp
index ff4e187ae..47e64492b 100644
--- a/qt-models/divepicturemodel.cpp
+++ b/qt-models/divepicturemodel.cpp
@@ -9,25 +9,28 @@
extern QHash <QString, QImage> thumbnailCache;
static QMutex thumbnailMutex;
+static const int maxZoom = 3; // Maximum zoom: thrice of standard size
-static void scaleImages(PictureEntry &entry)
+static QImage getThumbnailFromCache(const PictureEntry &entry)
{
QMutexLocker l(&thumbnailMutex);
- if (thumbnailCache.contains(entry.filename) && !thumbnailCache.value(entry.filename).isNull()) {
- entry.image = thumbnailCache.value(entry.filename);
- return;
- }
- l.unlock();
+ return thumbnailCache.value(entry.filename);
+}
- int dim = defaultIconMetrics().sz_pic;
- QImage p = SHashedImage(entry.picture);
- if(!p.isNull()) {
- p = p.scaled(dim, dim, Qt::KeepAspectRatio);
+static void scaleImages(PictureEntry &entry, int size, int maxSize)
+{
+ QImage thumbnail = getThumbnailFromCache(entry);
+ // If thumbnails were written by an earlier version, they might be smaller than needed.
+ // Rescale in such a case to avoid resizing artifacts.
+ if (thumbnail.isNull() || (thumbnail.size().width() < maxSize && thumbnail.size().height() < maxSize)) {
+ thumbnail = SHashedImage(entry.picture).scaled(maxSize, maxSize, Qt::KeepAspectRatio);
QMutexLocker l(&thumbnailMutex);
- if (!thumbnailCache.contains(entry.filename))
- thumbnailCache.insert(entry.filename, p);
+ thumbnailCache.insert(entry.filename, thumbnail);
}
- entry.image = p;
+
+ entry.imageProfile = thumbnail.scaled(maxSize / maxZoom, maxSize / maxZoom, Qt::KeepAspectRatio);
+ entry.image = size == maxSize ? thumbnail
+ : thumbnail.scaled(size, size, Qt::KeepAspectRatio);
}
DivePictureModel *DivePictureModel::instance()
@@ -36,7 +39,9 @@ DivePictureModel *DivePictureModel::instance()
return self;
}
-DivePictureModel::DivePictureModel() : rowDDStart(0), rowDDEnd(0)
+DivePictureModel::DivePictureModel() : rowDDStart(0),
+ rowDDEnd(0),
+ zoomLevel(0.0)
{
}
@@ -48,6 +53,33 @@ void DivePictureModel::updateDivePicturesWhenDone(QList<QFuture<void>> futures)
updateDivePictures();
}
+void DivePictureModel::setZoomLevel(int level)
+{
+ zoomLevel = level / 10.0;
+ // zoomLevel is bound by [-1.0 1.0], see comment below.
+ if (zoomLevel < -1.0)
+ zoomLevel = -1.0;
+ if (zoomLevel > 1.0)
+ zoomLevel = 1.0;
+ updateThumbnails();
+ layoutChanged();
+}
+
+void DivePictureModel::updateThumbnails()
+{
+ // Calculate size of thumbnails. The standard size is defaultIconMetrics().sz_pic.
+ // We use exponential scaling so that the central point is the standard
+ // size and the minimum and maximum extreme points are a third respectively
+ // three times the standard size.
+ // Naturally, these three zoom levels are then represented by
+ // -1.0 (minimum), 0 (standard) and 1.0 (maximum). The actual size is
+ // calculated as standard_size*3.0^zoomLevel.
+ int defaultSize = defaultIconMetrics().sz_pic;
+ int maxSize = defaultSize * maxZoom;
+ int size = static_cast<int>(round(defaultSize * pow(maxZoom, zoomLevel)));
+ QtConcurrent::blockingMap(pictures, [size, maxSize](PictureEntry &entry){scaleImages(entry, size, maxSize);});
+}
+
void DivePictureModel::updateDivePictures()
{
if (!pictures.isEmpty()) {
@@ -68,12 +100,13 @@ void DivePictureModel::updateDivePictures()
if (dive->id == displayed_dive.id)
rowDDStart = pictures.count();
FOR_EACH_PICTURE(dive)
- pictures.push_back({picture, picture->filename, QImage(), picture->offset.seconds});
+ pictures.push_back({picture, picture->filename, {}, {}, picture->offset.seconds});
if (dive->id == displayed_dive.id)
rowDDEnd = pictures.count();
}
}
- QtConcurrent::blockingMap(pictures, scaleImages);
+
+ updateThumbnails();
beginInsertRows(QModelIndex(), 0, pictures.count() - 1);
endInsertRows();
@@ -100,6 +133,9 @@ QVariant DivePictureModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
ret = entry.image;
break;
+ case Qt::UserRole: // Used by profile widget to access bigger thumbnails
+ ret = entry.imageProfile;
+ break;
case Qt::DisplayRole:
ret = QFileInfo(entry.filename).fileName();
break;
diff --git a/qt-models/divepicturemodel.h b/qt-models/divepicturemodel.h
index 53a72076a..74f92f449 100644
--- a/qt-models/divepicturemodel.h
+++ b/qt-models/divepicturemodel.h
@@ -10,6 +10,7 @@ struct PictureEntry {
struct picture *picture;
QString filename;
QImage image;
+ QImage imageProfile; // For the profile widget keep a copy of a constant sized image
int offsetSeconds;
};
@@ -24,9 +25,13 @@ public:
void updateDivePicturesWhenDone(QList<QFuture<void>>);
void removePicture(const QString& fileUrl, bool last);
int rowDDStart, rowDDEnd;
+public slots:
+ void setZoomLevel(int level);
private:
DivePictureModel();
QList<PictureEntry> pictures;
+ double zoomLevel; // -1.0: minimum, 0.0: standard, 1.0: maximum
+ void updateThumbnails();
};
#endif