diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2018-07-15 17:56:18 +0200 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2018-07-23 15:58:55 -0700 |
commit | b3feaa80e26510928c73e92049ec346841f6093a (patch) | |
tree | 1a1f002959ea09aa5a16a5cc04ace06326941377 /profile-widget | |
parent | c7428859840bfcf973a14f27c6376e39e915cb29 (diff) | |
download | subsurface-b3feaa80e26510928c73e92049ec346841f6093a.tar.gz |
Dive video: paint duration-bar above thumbnail in profile plot
Paint a rectangle on top of thumbnails indicating the run-time
of the video.
Use the z=100.0-101.0 range for painting the thumbnails, whereby
the z-value increases uniformly from first to last thumbnail
(sorted by timestamp). The duration-bars are placed at z-values
midway between those of the thumbnails.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'profile-widget')
-rw-r--r-- | profile-widget/divepixmapitem.cpp | 15 | ||||
-rw-r--r-- | profile-widget/divepixmapitem.h | 2 | ||||
-rw-r--r-- | profile-widget/profilewidget2.cpp | 102 | ||||
-rw-r--r-- | profile-widget/profilewidget2.h | 8 |
4 files changed, 103 insertions, 24 deletions
diff --git a/profile-widget/divepixmapitem.cpp b/profile-widget/divepixmapitem.cpp index d295fb4dd..730cdf606 100644 --- a/profile-widget/divepixmapitem.cpp +++ b/profile-widget/divepixmapitem.cpp @@ -42,7 +42,8 @@ void CloseButtonItem::show() DivePictureItem::DivePictureItem(QGraphicsItem *parent): DivePixmapItem(parent), canvas(new QGraphicsRectItem(this)), shadow(new QGraphicsRectItem(this)), - button(new CloseButtonItem(this)) + button(new CloseButtonItem(this)), + baseZValue(0.0) { setFlag(ItemIgnoresTransformations); setAcceptHoverEvents(true); @@ -67,6 +68,14 @@ DivePictureItem::DivePictureItem(QGraphicsItem *parent): DivePixmapItem(parent), button->hide(); } +// The base z-value is used for correct paint-order of the thumbnails. On hoverEnter the z-value is raised +// so that the thumbnail is drawn on top of all other thumbnails and on hoverExit it is restored to the base value. +void DivePictureItem::setBaseZValue(double z) +{ + baseZValue = z; + setZValue(z); +} + void DivePictureItem::settingsChanged() { setVisible(prefs.show_pictures_in_profile); @@ -85,7 +94,7 @@ void DivePictureItem::setPixmap(const QPixmap &pix) void DivePictureItem::hoverEnterEvent(QGraphicsSceneHoverEvent*) { Animations::scaleTo(this, 1.0); - setZValue(5); + setZValue(baseZValue + 5.0); button->setOpacity(0); button->show(); @@ -100,7 +109,7 @@ void DivePictureItem::setFileUrl(const QString &s) void DivePictureItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { Animations::scaleTo(this, 0.2); - setZValue(0); + setZValue(baseZValue); Animations::hide(button); } diff --git a/profile-widget/divepixmapitem.h b/profile-widget/divepixmapitem.h index 6abe410a6..f8b922981 100644 --- a/profile-widget/divepixmapitem.h +++ b/profile-widget/divepixmapitem.h @@ -32,6 +32,7 @@ class DivePictureItem : public DivePixmapItem { public: DivePictureItem(QGraphicsItem *parent = 0); void setPixmap(const QPixmap& pix); + void setBaseZValue(double z); public slots: void settingsChanged(); void removePicture(); @@ -45,6 +46,7 @@ private: QGraphicsRectItem *canvas; QGraphicsRectItem *shadow; CloseButtonItem *button; + double baseZValue; }; #endif // DIVEPIXMAPITEM_H diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 11dddaa41..79aa2e876 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -87,6 +87,10 @@ static struct _ItemPos { _Axis heartBeatWithTankBar; } itemPos; +// Constant describing at which z-level the thumbnails are located. +// We might add more constants here for easier customability. +static const double thumbnailBaseZValue = 100.0; + ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent), currentState(INVALID), dataModel(new DivePlotDataModel(this)), @@ -975,6 +979,21 @@ void ProfileWidget2::fixBackgroundPos() background->setY(mapToScene(y, 20).y()); } +void ProfileWidget2::scale(qreal sx, qreal sy) +{ + QGraphicsView::scale(sx, sy); + +#ifndef SUBSURFACE_MOBILE + // Since the zoom level changed, adjust the duration bars accordingly. + // We want to grow/shrink the length, but not the height and pen. + for (PictureEntry &p: pictures) + updateDurationLine(p); + + // Since we created new duration lines, we have to update the order in which the thumbnails is painted. + updateThumbnailPaintOrder(); +#endif +} + #ifndef SUBSURFACE_MOBILE void ProfileWidget2::wheelEvent(QWheelEvent *event) { @@ -2064,9 +2083,37 @@ void ProfileWidget2::clearPictures() pictures.clear(); } +static const double unscaledDurationLineWidth = 2.5; +static const double unscaledDurationLinePenWidth = 0.5; + +// Reset the duration line after an image was moved or we found a new duration +void ProfileWidget2::updateDurationLine(PictureEntry &e) +{ + if (e.duration.seconds > 0) { + // We know the duration of this video, reset the line symbolizing its extent accordingly + double begin = timeAxis->posAtValue(e.offset.seconds); + double end = timeAxis->posAtValue(e.offset.seconds + e.duration.seconds); + double y = e.thumbnail->y(); + + // Undo scaling for pen-width and line-width. For this purpose, we use the scaling of the y-axis. + double scale = transform().m22(); + double durationLineWidth = unscaledDurationLineWidth / scale; + double durationLinePenWidth = unscaledDurationLinePenWidth / scale; + e.durationLine.reset(new QGraphicsRectItem(begin, y - durationLineWidth - durationLinePenWidth, end - begin, durationLineWidth)); + e.durationLine->setPen(QPen(getColor(GF_LINE, isGrayscale), durationLinePenWidth)); + e.durationLine->setBrush(getColor(::BACKGROUND, isGrayscale)); + e.durationLine->setVisible(prefs.show_pictures_in_profile); + scene()->addItem(e.durationLine.get()); + } else { + // This is either a picture or a video with unknown duration. + // In case there was a line (how could that be?) remove it. + e.durationLine.reset(); + } +} + // This function is called asynchronously by the thumbnailer if a thumbnail // was fetched from disk or freshly calculated. -void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail) +void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail, duration_t duration) { // Find the picture with the given filename auto it = std::find_if(pictures.begin(), pictures.end(), [&filename](const PictureEntry &e) @@ -2078,11 +2125,20 @@ void ProfileWidget2::updateThumbnail(QString filename, QImage thumbnail) // Replace the pixmap of the thumbnail with the newly calculated one. int size = Thumbnailer::defaultThumbnailSize(); it->thumbnail->setPixmap(QPixmap::fromImage(thumbnail.scaled(size, size, Qt::KeepAspectRatio))); + + // If the duration changed, update the line + if (duration.seconds != it->duration.seconds) { + it->duration = duration; + updateDurationLine(*it); + // If we created / removed a duration line, we have to update the thumbnail paint order. + updateThumbnailPaintOrder(); + } } } // Create a PictureEntry object and add its thumbnail to the scene if profile pictures are shown. ProfileWidget2::PictureEntry::PictureEntry(offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene) : offset(offsetIn), + duration(duration_t {0}), filename(filenameIn), thumbnail(new DivePictureItem) { @@ -2101,23 +2157,37 @@ bool ProfileWidget2::PictureEntry::operator< (const PictureEntry &e) const return std::tie(offset.seconds, filename) < std::tie(e.offset.seconds, e.filename); } +// This function updates the paint order of the thumbnails and duration-lines, such that later +// thumbnails are painted on top of previous thumbnails and duration-lines on top of the thumbnail +// they belong to. +void ProfileWidget2::updateThumbnailPaintOrder() +{ + if (!pictures.size()) + return; + // To get the correct sort order, we place in thumbnails at equal z-distances + // between thumbnailBaseZValue and (thumbnailBaseZValue + 1.0). + // Duration-lines are placed between the thumbnails. + double z = thumbnailBaseZValue; + double step = 1.0 / (double)pictures.size(); + for (PictureEntry &e: pictures) { + e.thumbnail->setBaseZValue(z); + if (e.durationLine) + e.durationLine->setZValue(z + step / 2.0); + z += step; + } +} + // Calculate the y-coordinates of the thumbnails, which are supposed to be sorted by x-coordinate. // This will also change the order in which the thumbnails are painted, to avoid weird effects, // when items are added later to the scene. This is done using the QGraphicsItem::packBefore() function. // We can't use the z-value, because that will be modified on hoverEnter and hoverExit events. void ProfileWidget2::calculatePictureYPositions() { - // Quit early if there are no items. The last loop in this function assumes that the vector is not empty. - if (pictures.empty()) - return; - double lastX = -1.0, lastY = 0.0; - for (auto it = pictures.begin(); it != pictures.end(); ++it) { - if (!it->thumbnail) - continue; + for (PictureEntry &e: pictures) { // 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. - double x = it->thumbnail->x(); + double x = e.thumbnail->x(); double y; if (lastX >= 0.0 && fabs(x - lastX) < 3 && lastY <= (10 + 14 * 3)) y = lastY + 3; @@ -2125,18 +2195,10 @@ void ProfileWidget2::calculatePictureYPositions() y = 10; lastX = x; lastY = y; - it->thumbnail->setY(y); - - // hoverEnter and hoverExit events modify the z-value. Objects with different z-values - // are not considered in stackBefore() calls. Therefore, just to be sure, reset the - // z-values of all picture entries. - it->thumbnail->setZValue(0.0); + e.thumbnail->setY(y); + updateDurationLine(e); // If we changed the y-position, we also have to change the duration-line. } - - // Plot the items in the correct order. Experience showed that this works only - // if we rearrange the items starting from the back. Therefore, use rbegin() and rend(). - for (auto it = pictures.rbegin(); std::next(it) != pictures.rend(); ++it) - std::next(it)->thumbnail->stackBefore(it->thumbnail.get()); + updateThumbnailPaintOrder(); } void ProfileWidget2::updateThumbnailXPos(PictureEntry &e) diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index 2640fb85a..bbad5b7cc 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -74,6 +74,7 @@ public: ProfileWidget2(QWidget *parent = 0); void resetZoom(); + void scale(qreal sx, qreal sy); void plotDive(struct dive *d = 0, bool force = false, bool clearPictures = false); void setupItem(AbstractProfilePolygonItem *item, DiveCartesianAxis *vAxis, int vData, int hData, int zValue); void setPrintMode(bool mode, bool grayscale = false); @@ -127,7 +128,7 @@ slots: // Necessary to call from QAction's signals. void deleteCurrentDC(); void pointInserted(const QModelIndex &parent, int start, int end); void pointsRemoved(const QModelIndex &, int start, int end); - void updateThumbnail(QString filename, QImage thumbnail); + void updateThumbnail(QString filename, QImage thumbnail, duration_t duration); /* this is called for every move on the handlers. maybe we can speed up this a bit? */ void recreatePlannedDive(); @@ -234,14 +235,19 @@ private: // Pictures that are outside of the dive time are not shown. struct PictureEntry { offset_t offset; + duration_t duration; QString filename; std::unique_ptr<DivePictureItem> thumbnail; + // For videos with known duration, we represent the duration of the video by a line + std::unique_ptr<QGraphicsRectItem> durationLine; PictureEntry (offset_t offsetIn, const QString &filenameIn, QGraphicsScene *scene); bool operator< (const PictureEntry &e) const; }; void updateThumbnailXPos(PictureEntry &e); std::vector<PictureEntry> pictures; void calculatePictureYPositions(); + void updateDurationLine(PictureEntry &e); + void updateThumbnailPaintOrder(); QList<DiveHandler *> handles; void repositionDiveHandlers(); |