#include "library/dlgcoverartfullsize.h" #include #include #include #include #include "library/coverartcache.h" #include "library/coverartutils.h" #include "moc_dlgcoverartfullsize.cpp" #include "track/track.h" #include "util/widgethelper.h" DlgCoverArtFullSize::DlgCoverArtFullSize(QWidget* parent, BaseTrackPlayer* pPlayer) : QDialog(parent), m_pPlayer(pPlayer), m_pCoverMenu(make_parented(this)) { CoverArtCache* pCache = CoverArtCache::instance(); if (pCache) { connect(pCache, &CoverArtCache::coverFound, this, &DlgCoverArtFullSize::slotCoverFound); } setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &DlgCoverArtFullSize::customContextMenuRequested, this, &DlgCoverArtFullSize::slotCoverMenu); connect(m_pCoverMenu, &WCoverArtMenu::coverInfoSelected, this, &DlgCoverArtFullSize::slotCoverInfoSelected); connect(m_pCoverMenu, &WCoverArtMenu::reloadCoverArt, this, &DlgCoverArtFullSize::slotReloadCoverArt); if (m_pPlayer != nullptr) { connect(pPlayer, &BaseTrackPlayer::newTrackLoaded, this, &DlgCoverArtFullSize::slotLoadTrack); } setupUi(this); } void DlgCoverArtFullSize::closeEvent(QCloseEvent* event) { if (parentWidget()) { // Since the widget has a parent, this instance will be reused again. // We need to prevent qt from destroying it's children hide(); slotLoadTrack(nullptr); event->ignore(); } else { QDialog::closeEvent(event); } } void DlgCoverArtFullSize::init(TrackPointer pTrack) { if (!pTrack) { return; } // The real size will be calculated later. // If you zoom in so the window is larger then the desktop, close the // window and reopen, the window will not be resized correctly // by slotCoverFound. Setting a small fixed size before show fixes this resize(100, 100); show(); raise(); activateWindow(); // This must be called after show() to set the window title. Refer to the // comment in slotLoadTrack for details. slotLoadTrack(pTrack); } void DlgCoverArtFullSize::slotLoadTrack(TrackPointer pTrack) { if (m_pLoadedTrack != nullptr) { disconnect(m_pLoadedTrack.get(), &Track::coverArtUpdated, this, &DlgCoverArtFullSize::slotTrackCoverArtUpdated); } m_pLoadedTrack = pTrack; if (m_pLoadedTrack != nullptr) { connect(m_pLoadedTrack.get(), &Track::coverArtUpdated, this, &DlgCoverArtFullSize::slotTrackCoverArtUpdated); // Somehow setting the widow title triggered a bug in Xlib that resulted // in a deadlock before the check for isVisible() was added. // Unfortunately the original bug was difficult to reproduce, so I am // not sure if checking isVisible() before setting the window title // actually works around the Xlib bug or merely makes it much less // likely to be triggered. Before the isVisible() check was added, // the window title was getting set on DlgCoverArtFullSize instances // that had never been shown whenever a track was loaded. // https://bugs.launchpad.net/mixxx/+bug/1789059 // https://gitlab.freedesktop.org/xorg/lib/libx11/issues/25#note_50985 if (isVisible()) { QString windowTitle; const QString albumArtist = m_pLoadedTrack->getAlbumArtist(); const QString artist = m_pLoadedTrack->getArtist(); const QString album = m_pLoadedTrack->getAlbum(); const QString year = m_pLoadedTrack->getYear(); if (!albumArtist.isEmpty()) { windowTitle = albumArtist; } else if (!artist.isEmpty()) { windowTitle += artist; } if (!album.isEmpty()) { if (!windowTitle.isEmpty()) { windowTitle += " - "; } windowTitle += album; } if (!year.isEmpty()) { if (!windowTitle.isEmpty()) { windowTitle += " "; } windowTitle += QString("(%1)").arg(year); } setWindowTitle(windowTitle); } } slotTrackCoverArtUpdated(); } void DlgCoverArtFullSize::slotTrackCoverArtUpdated() { if (m_pLoadedTrack) { CoverArtCache::requestTrackCover(this, m_pLoadedTrack); } else { coverArt->setPixmap(QPixmap()); } } void DlgCoverArtFullSize::slotCoverFound( const QObject* pRequestor, const CoverInfo& coverInfo, const QPixmap& pixmap, mixxx::cache_key_t requestedCacheKey, bool coverInfoUpdated) { Q_UNUSED(requestedCacheKey); Q_UNUSED(coverInfoUpdated); if (pRequestor != this || !m_pLoadedTrack || m_pLoadedTrack->getLocation() != coverInfo.trackLocation) { return; } m_pixmap = pixmap; if (m_pixmap.isNull()) { coverArt->setPixmap(QPixmap()); hide(); return; } // Scale down dialog if the pixmap is larger than the screen. // Use 90% of screen size instead of 100% to prevent an issue with // whitespace appearing on the side when resizing a window whose // borders touch the edges of the screen. QSize dialogSize = m_pixmap.size(); QWidget* centerOverWidget = parentWidget(); VERIFY_OR_DEBUG_ASSERT(centerOverWidget) { qWarning() << "DlgCoverArtFullSize does not have a parent."; centerOverWidget = this; } const QScreen* const pScreen = mixxx::widgethelper::getScreen(*centerOverWidget); QRect screenGeometry; VERIFY_OR_DEBUG_ASSERT(pScreen) { qWarning() << "Assuming screen size of 800x600px."; screenGeometry = QRect(0, 0, 800, 600); } else { screenGeometry = pScreen->geometry(); } const QSize availableScreenSpace = screenGeometry.size() * 0.9; if (dialogSize.height() > availableScreenSpace.height()) { dialogSize.scale(dialogSize.width(), screenGeometry.height(), Qt::KeepAspectRatio); } else if (dialogSize.width() > screenGeometry.width()) { dialogSize.scale(screenGeometry.width(), dialogSize.height(), Qt::KeepAspectRatio); } QPixmap resizedPixmap = m_pixmap.scaled(size() * getDevicePixelRatioF(this), Qt::KeepAspectRatio, Qt::SmoothTransformation); resizedPixmap.setDevicePixelRatio(getDevicePixelRatioF(this)); coverArt->setPixmap(resizedPixmap); // center the window setGeometry(QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, dialogSize, screenGeometry)); } // slots to handle signals from the context menu void DlgCoverArtFullSize::slotReloadCoverArt() { if (!m_pLoadedTrack) { return; } slotCoverInfoSelected( CoverInfoGuesser().guessCoverInfoForTrack( *m_pLoadedTrack)); } void DlgCoverArtFullSize::slotCoverInfoSelected( const CoverInfoRelative& coverInfo) { if (!m_pLoadedTrack) { return; } m_pLoadedTrack->setCoverInfo(coverInfo); } void DlgCoverArtFullSize::mousePressEvent(QMouseEvent* event) { if (!m_pCoverMenu->isVisible() && event->button() == Qt::LeftButton) { m_clickTimer.setSingleShot(true); m_clickTimer.start(500); m_coverPressed = true; m_dragStartPosition = event->globalPos() - frameGeometry().topLeft(); } } void DlgCoverArtFullSize::mouseReleaseEvent(QMouseEvent* event) { m_coverPressed = false; if (m_pCoverMenu->isVisible()) { return; } if (event->button() == Qt::LeftButton && isVisible()) { if (m_clickTimer.isActive()) { // short press close(); } else { // long press return; } event->accept(); } } void DlgCoverArtFullSize::mouseMoveEvent(QMouseEvent* event) { if (m_coverPressed) { move(event->globalPos() - m_dragStartPosition); event->accept(); } else { return; } } void DlgCoverArtFullSize::slotCoverMenu(const QPoint& pos) { m_pCoverMenu->popup(mapToGlobal(pos)); } void DlgCoverArtFullSize::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); if (m_pixmap.isNull()) { return; } // qDebug() << "DlgCoverArtFullSize::resizeEvent" << size(); QPixmap resizedPixmap = m_pixmap.scaled(size() * getDevicePixelRatioF(this), Qt::KeepAspectRatio, Qt::SmoothTransformation); resizedPixmap.setDevicePixelRatio(getDevicePixelRatioF(this)); coverArt->setPixmap(resizedPixmap); } void DlgCoverArtFullSize::wheelEvent(QWheelEvent* event) { // Scale the image size int oldWidth = width(); int oldHeight = height(); auto newWidth = static_cast(oldWidth + (0.2 * event->angleDelta().y())); auto newHeight = static_cast(oldHeight + (0.2 * event->angleDelta().y())); QSize newSize = size(); newSize.scale(newWidth, newHeight, Qt::KeepAspectRatio); // To keep the same part of the image under the cursor, shift the // origin (top left point) by the distance the point moves under the cursor. QPoint oldOrigin = geometry().topLeft(); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QPoint oldPointUnderCursor = event->position().toPoint(); #else QPoint oldPointUnderCursor = event->pos(); #endif const auto newPointX = static_cast( static_cast(oldPointUnderCursor.x()) / oldWidth * newSize.width()); const auto newPointY = static_cast( static_cast(oldPointUnderCursor.y()) / oldHeight * newSize.height()); QPoint newOrigin = QPoint( oldOrigin.x() + (oldPointUnderCursor.x() - newPointX), oldOrigin.y() + (oldPointUnderCursor.y() - newPointY)); // Calling resize() then move() causes flickering, so resize and move the window // simultaneously with setGeometry(). setGeometry(QRect(newOrigin, newSize)); event->accept(); }