Changeset 05a1626 in subsurface


Ignore:
Timestamp:
Dec 18, 2017, 12:07:10 PM (5 weeks ago)
Author:
Dirk Hohndel <dirk@…>
Branches:
master
Children:
54feaa8
Parents:
0d01c70
git-author:
Berthold Stoeger <bstoeger@…> (12/17/17 07:17:38)
git-committer:
Dirk Hohndel <dirk@…> (12/18/17 12:07:10)
Message:

Implement different zoom levels for dive photos tab

This implements different zoom levels for the dive photos tab as
suggested by Stefan Fuchs <sfuchs@…> in #898.
The zoom level can be changed using a slider or CTRL+mousewheel.
Zoom levels range from a third of the standard thumbnail size to
thrice the standard thumbnail size.

Thumbnails are cached in maximum resolution and scaled down on
the fly. Because the profile widget took its pictures from the
photo list model, an extra picture copy with a fixed size had
to be introduced.

The UI is still a bit crude.

Reported-by: Stefan Fuchs <sfuchs@…>
Signed-off-by: Berthold Stoeger <bstoeger@…>

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • desktop-widgets/divepicturewidget.cpp

    r1f49608 r05a1626  
    6060        }
    6161}
     62
     63void DivePictureWidget::wheelEvent(QWheelEvent *event)
     64{
     65        if (event->modifiers() == Qt::ControlModifier) {
     66                // Angle delta is given in eighth parts of a degree. A classical mouse
     67                // wheel click is 15 degrees. Each click should correspond to one zoom step.
     68                // Therefore, divide by 15*8=120. To also support touch pads and finer-grained
     69                // mouse wheels, take care to always round away from zero.
     70                int delta = event->angleDelta().y();
     71                int carry = delta > 0 ? 119 : -119;
     72                emit zoomLevelChanged((delta + carry) / 120);
     73        } else
     74                QListView::wheelEvent(event);
     75}
  • desktop-widgets/divepicturewidget.h

    ra1e6ac2 r05a1626  
    1515        void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    1616        void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
     17        void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;
    1718
    1819signals:
    1920        void photoDoubleClicked(const QString filePath);
     21        void zoomLevelChanged(int delta);
    2022};
    2123
  • desktop-widgets/tab-widgets/TabDivePhotos.cpp

    r1b7ee87 r05a1626  
    3030                }
    3131        );
     32        connect(ui->photosView, &DivePictureWidget::zoomLevelChanged,
     33                this, &TabDivePhotos::changeZoomLevel);
     34        connect(ui->zoomSlider, &QAbstractSlider::valueChanged,
     35                DivePictureModel::instance(), &DivePictureModel::setZoomLevel);
    3236}
    3337
     
    99103}
    100104
     105void TabDivePhotos::changeZoomLevel(int delta)
     106{
     107        // We count on QSlider doing bound checks
     108        ui->zoomSlider->setValue(ui->zoomSlider->value() + delta);
     109}
  • desktop-widgets/tab-widgets/TabDivePhotos.h

    r1b8b112 r05a1626  
    2727        void removeAllPhotos();
    2828        void removeSelectedPhotos();
     29        void changeZoomLevel(int delta);
    2930
    3031private:
  • desktop-widgets/tab-widgets/TabDivePhotos.ui

    r1fc4fba r05a1626  
    2222    </widget>
    2323   </item>
     24   <item>
     25    <layout class="QHBoxLayout" name="zoomLayout">
     26     <item>
     27      <widget class="QLabel" name="zoomLabel">
     28       <property name="text">
     29        <string>Zoom level</string>
     30       </property>
     31      </widget>
     32     </item>
     33     <item>
     34      <widget class="QSlider" name="zoomSlider">
     35       <property name="minimum">
     36        <number>-10</number>
     37       </property>
     38       <property name="maximum">
     39        <number>10</number>
     40       </property>
     41       <property name="singleStep">
     42        <number>1</number>
     43       </property>
     44       <property name="pageStep">
     45        <number>1</number>
     46       </property>
     47       <property name="orientation">
     48        <enum>Qt::Horizontal</enum>
     49       </property>
     50       <property name="tickPosition">
     51        <enum>QSlider::TicksBelow</enum>
     52       </property>
     53       <property name="tickInterval">
     54        <number>1</number>
     55       </property>
     56      </widget>
     57     </item>
     58    </layout>
     59   </item>
    2460  </layout>
    2561 </widget>
  • profile-widget/profilewidget2.cpp

    r2890be9 r05a1626  
    20082008                        continue;
    20092009                DivePictureItem *item = new DivePictureItem();
    2010                 item->setPixmap(m->index(i, 0).data(Qt::DecorationRole).value<QPixmap>());
     2010                item->setPixmap(m->index(i, 0).data(Qt::UserRole).value<QPixmap>());
    20112011                item->setFileUrl(m->index(i, 1).data().toString());
    20122012                // let's put the picture at the correct time, but at a fixed "depth" on the profile
  • qt-models/divepicturemodel.cpp

    r2f5f1e0 r05a1626  
    1010extern QHash <QString, QImage> thumbnailCache;
    1111static QMutex thumbnailMutex;
     12static const int maxZoom = 3;   // Maximum zoom: thrice of standard size
    1213
    13 static void scaleImages(PictureEntry &entry)
     14static QImage getThumbnailFromCache(const PictureEntry &entry)
    1415{
    1516        QMutexLocker l(&thumbnailMutex);
    16         if (thumbnailCache.contains(entry.filename) && !thumbnailCache.value(entry.filename).isNull()) {
    17                 entry.image = thumbnailCache.value(entry.filename);
    18                 return;
     17        return thumbnailCache.value(entry.filename);
     18}
     19
     20static void scaleImages(PictureEntry &entry, int size, int maxSize)
     21{
     22        QImage thumbnail = getThumbnailFromCache(entry);
     23        // If thumbnails were written by an earlier version, they might be smaller than needed.
     24        // Rescale in such a case to avoid resizing artifacts.
     25        if (thumbnail.isNull() || (thumbnail.size().width() < maxSize && thumbnail.size().height() < maxSize)) {
     26                thumbnail = SHashedImage(entry.picture).scaled(maxSize, maxSize, Qt::KeepAspectRatio);
     27                QMutexLocker l(&thumbnailMutex);
     28                thumbnailCache.insert(entry.filename, thumbnail);
    1929        }
    20         l.unlock();
    2130
    22         int dim = defaultIconMetrics().sz_pic;
    23         QImage p = SHashedImage(entry.picture);
    24         if(!p.isNull()) {
    25                 p = p.scaled(dim, dim, Qt::KeepAspectRatio);
    26                 QMutexLocker l(&thumbnailMutex);
    27                 if (!thumbnailCache.contains(entry.filename))
    28                         thumbnailCache.insert(entry.filename, p);
    29         }
    30         entry.image = p;
     31        entry.imageProfile = thumbnail.scaled(maxSize / maxZoom, maxSize / maxZoom, Qt::KeepAspectRatio);
     32        entry.image = size == maxSize ? thumbnail
     33                                      : thumbnail.scaled(size, size, Qt::KeepAspectRatio);
    3134}
    3235
     
    3740}
    3841
    39 DivePictureModel::DivePictureModel() : rowDDStart(0), rowDDEnd(0)
     42DivePictureModel::DivePictureModel() : rowDDStart(0),
     43                                       rowDDEnd(0),
     44                                       zoomLevel(0.0)
    4045{
    4146}
     
    4752        }
    4853        updateDivePictures();
     54}
     55
     56void DivePictureModel::setZoomLevel(int level)
     57{
     58        zoomLevel = level / 10.0;
     59        // zoomLevel is bound by [-1.0 1.0], see comment below.
     60        if (zoomLevel < -1.0)
     61                zoomLevel = -1.0;
     62        if (zoomLevel > 1.0)
     63                zoomLevel = 1.0;
     64        updateThumbnails();
     65        layoutChanged();
     66}
     67
     68void DivePictureModel::updateThumbnails()
     69{
     70        // Calculate size of thumbnails. The standard size is defaultIconMetrics().sz_pic.
     71        // We use exponential scaling so that the central point is the standard
     72        // size and the minimum and maximum extreme points are a third respectively
     73        // three times the standard size.
     74        // Naturally, these three zoom levels are then represented by
     75        // -1.0 (minimum), 0 (standard) and 1.0 (maximum). The actual size is
     76        // calculated as standard_size*3.0^zoomLevel.
     77        int defaultSize = defaultIconMetrics().sz_pic;
     78        int maxSize = defaultSize * maxZoom;
     79        int size = static_cast<int>(round(defaultSize * pow(maxZoom, zoomLevel)));
     80        QtConcurrent::blockingMap(pictures, [size, maxSize](PictureEntry &entry){scaleImages(entry, size, maxSize);});
    4981}
    5082
     
    69101                                rowDDStart = pictures.count();
    70102                        FOR_EACH_PICTURE(dive)
    71                                 pictures.push_back({picture, picture->filename, QImage(), picture->offset.seconds});
     103                                pictures.push_back({picture, picture->filename, {}, {}, picture->offset.seconds});
    72104                        if (dive->id == displayed_dive.id)
    73105                                rowDDEnd = pictures.count();
    74106                }
    75107        }
    76         QtConcurrent::blockingMap(pictures, scaleImages);
     108
     109        updateThumbnails();
    77110
    78111        beginInsertRows(QModelIndex(), 0, pictures.count() - 1);
     
    100133                case Qt::DecorationRole:
    101134                        ret = entry.image;
     135                        break;
     136                case Qt::UserRole:      // Used by profile widget to access bigger thumbnails
     137                        ret = entry.imageProfile;
    102138                        break;
    103139                case Qt::DisplayRole:
  • qt-models/divepicturemodel.h

    r0d01c70 r05a1626  
    1111        QString filename;
    1212        QImage image;
     13        QImage imageProfile;    // For the profile widget keep a copy of a constant sized image
    1314        int offsetSeconds;
    1415};
     
    2526        void removePicture(const QString& fileUrl, bool last);
    2627        int rowDDStart, rowDDEnd;
     28public slots:
     29        void setZoomLevel(int level);
    2730private:
    2831        DivePictureModel();
    2932        QList<PictureEntry> pictures;
     33        double zoomLevel;       // -1.0: minimum, 0.0: standard, 1.0: maximum
     34        void updateThumbnails();
    3035};
    3136
Note: See TracChangeset for help on using the changeset viewer.