diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ Widgets Gui DBus + Multimedia + MultimediaWidgets ) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -277,7 +277,8 @@ KF5::KCMUtils KF5::DBusAddons KF5::Notifications - Phonon::phonon4qt5 + Qt5::Multimedia + Qt5::MultimediaWidgets ) if (HAVE_KACTIVITIES) diff --git a/src/panels/information/phononwidget.h b/src/panels/information/phononwidget.h --- a/src/panels/information/phononwidget.h +++ b/src/panels/information/phononwidget.h @@ -26,14 +26,13 @@ #include #include #include +#include +#include -namespace Phonon -{ - class AudioOutput; - class MediaObject; - class SeekSlider; - class VideoPlayer; -} // namespace Phonon +class QMediaPlaylist; +class QMediaPlayer; +class QVideoWidget; +class SeekSlider; class EmbeddedVideoPlayer; class QToolButton; @@ -78,12 +77,15 @@ void hideEvent(QHideEvent *event) override; private slots: - void stateChanged(Phonon::State newstate); + void stateChanged(QMediaPlayer::State newstate); void stop(); void finished(); private: void applyVideoSize(); + void setPosition(qint64 position); + void positionChanged(qint64 position); + void durationChanged(qint64 position); private: QUrl m_url; @@ -93,10 +95,10 @@ QToolButton *m_stopButton; QVBoxLayout *m_topLayout; - Phonon::MediaObject *m_media; - Phonon::SeekSlider *m_seekSlider; - Phonon::AudioOutput *m_audioOutput; - EmbeddedVideoPlayer *m_videoPlayer; + QMediaPlayer *m_player; + QSlider *m_seekSlider; + EmbeddedVideoPlayer *m_videoWidget; + QMediaPlaylist *m_playlist; bool m_autoPlay; bool m_isVideo; }; diff --git a/src/panels/information/phononwidget.cpp b/src/panels/information/phononwidget.cpp --- a/src/panels/information/phononwidget.cpp +++ b/src/panels/information/phononwidget.cpp @@ -22,22 +22,23 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include +#include -class EmbeddedVideoPlayer : public Phonon::VideoWidget +class EmbeddedVideoPlayer : public QVideoWidget { Q_OBJECT public: EmbeddedVideoPlayer(QWidget *parent = nullptr) : - Phonon::VideoWidget(parent) + QVideoWidget(parent) { } @@ -49,31 +50,98 @@ QSize sizeHint() const override { - return m_sizeHint.isValid() ? m_sizeHint : Phonon::VideoWidget::sizeHint(); + return m_sizeHint.isValid() ? m_sizeHint : QVideoWidget::sizeHint(); } private: QSize m_sizeHint; }; +class SeekSlider : public QSlider +{ + Q_OBJECT + + public: + SeekSlider(Qt::Orientation orientation, QWidget *parent = nullptr): QSlider(orientation, parent) + {} + + protected: + // Function copied from qslider.cpp + inline int pick(const QPoint &pt) const + { + return orientation() == Qt::Horizontal ? pt.x() : pt.y(); + } + + // Function copied from qslider.cpp and modified to make it compile + int pixelPosToRangeValue(int pos) const + { + QStyleOptionSlider opt; + initStyleOption(&opt); + QRect gr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this); + QRect sr = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + int sliderMin, sliderMax, sliderLength; + + if (orientation() == Qt::Horizontal) { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } else { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + return QStyle::sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, + sliderMax - sliderMin, opt.upsideDown); + } + + // Based on code from qslider.cpp + void mousePressEvent ( QMouseEvent * event ) override + { + if (event->button() == Qt::LeftButton) { + QStyleOptionSlider opt; + initStyleOption(&opt); + const QRect sliderRect = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this); + const QPoint center = sliderRect.center() - sliderRect.topLeft(); + // to take half of the slider off for the setSliderPosition call we use the center - topLeft + + if (!sliderRect.contains(event->pos())) { + event->accept(); + + int position = pixelPosToRangeValue(pick(event->pos() - center)); + setSliderPosition(position); + triggerAction(SliderMove); + setRepeatAction(SliderNoAction); + + emit sliderMoved(position); + } else { + QSlider::mousePressEvent(event); + } + } else { + QSlider::mousePressEvent(event); + } + } +}; + PhononWidget::PhononWidget(QWidget *parent) : QWidget(parent), m_url(), m_playButton(nullptr), m_stopButton(nullptr), m_topLayout(nullptr), - m_media(nullptr), + m_player(nullptr), m_seekSlider(nullptr), - m_audioOutput(nullptr), - m_videoPlayer(nullptr) + m_videoWidget(nullptr), + m_playlist(nullptr) { } void PhononWidget::setUrl(const QUrl &url, MediaKind kind) { if (m_url != url) { m_url = url; m_isVideo = kind == MediaKind::Video; + m_seekSlider->setValue(0); + m_seekSlider->setSliderPosition(0); } if (m_autoPlay) { play(); @@ -85,7 +153,7 @@ void PhononWidget::setAutoPlay(bool autoPlay) { m_autoPlay = autoPlay; - if (!m_url.isEmpty() && (m_media == nullptr || m_media->state() != Phonon::State::PlayingState) && m_autoPlay && isVisible()) { + if (!m_url.isEmpty() && (m_player == nullptr || m_player->state() != QMediaPlayer::PlayingState) && m_autoPlay && isVisible()) { play(); } } @@ -125,7 +193,7 @@ m_playButton = new QToolButton(this); m_stopButton = new QToolButton(this); - m_seekSlider = new Phonon::SeekSlider(this); + m_seekSlider = new SeekSlider(Qt::Orientation::Horizontal, this); controlsLayout->addWidget(m_playButton); controlsLayout->addWidget(m_stopButton); @@ -149,8 +217,6 @@ m_stopButton->hide(); connect(m_stopButton, &QToolButton::clicked, this, &PhononWidget::stop); - m_seekSlider->setIconVisible(false); - // Creating an audio player or video player instance might take up to // 2 seconds when doing it the first time. To prevent that the user // interface gets noticeable blocked, the creation is delayed until @@ -166,12 +232,12 @@ } } -void PhononWidget::stateChanged(Phonon::State newstate) +void PhononWidget::stateChanged(QMediaPlayer::State newstate) { setUpdatesEnabled(false); switch (newstate) { - case Phonon::PlayingState: - case Phonon::BufferingState: + case QMediaPlayer::State::PlayingState: + case QMediaPlayer::State::PausedState: m_stopButton->show(); m_playButton->hide(); break; @@ -185,62 +251,83 @@ void PhononWidget::play() { - if (!m_media) { - m_media = new Phonon::MediaObject(this); - connect(m_media, &Phonon::MediaObject::stateChanged, - this, &PhononWidget::stateChanged); - connect(m_media, &Phonon::MediaObject::finished, - this, &PhononWidget::finished); - m_seekSlider->setMediaObject(m_media); - } + if (!m_playlist) { + m_playlist = new QMediaPlaylist; + + m_player = new QMediaPlayer; + m_player->setPlaylist(m_playlist); - if (!m_videoPlayer) { - m_videoPlayer = new EmbeddedVideoPlayer(this); - m_videoPlayer->installEventFilter(this); - m_topLayout->insertWidget(0, m_videoPlayer); - Phonon::createPath(m_media, m_videoPlayer); + m_videoWidget = new EmbeddedVideoPlayer(this); + m_player->setVideoOutput(m_videoWidget); + m_topLayout->insertWidget(0, m_videoWidget); applyVideoSize(); - } - if (!m_audioOutput) { - m_audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this); - Phonon::createPath(m_media, m_audioOutput); + connect(m_seekSlider, &QAbstractSlider::sliderMoved, + this, &PhononWidget::setPosition); + + connect(m_player, &QMediaPlayer::stateChanged, this, &PhononWidget::stateChanged); + connect(m_player, &QMediaPlayer::positionChanged, this, &PhononWidget::positionChanged); + connect(m_player, &QMediaPlayer::durationChanged, this, &PhononWidget::durationChanged); } if (m_isVideo) { emit hasVideoChanged(true); } - if (m_url != m_media->currentSource().url()) { - m_media->setCurrentSource(m_url); + if (m_url != m_playlist->currentMedia().canonicalUrl()) { + m_playlist->addMedia(m_url); + m_playlist->next(); } - m_media->play(); + m_player->play(); - m_videoPlayer->setVisible(m_isVideo); + m_videoWidget->setVisible(m_isVideo); } void PhononWidget::finished() { if (m_isVideo) { - m_videoPlayer->hide(); + m_videoWidget->hide(); emit hasVideoChanged(false); } } void PhononWidget::stop() { - if (m_media) { - m_media->stop(); - m_videoPlayer->hide(); + if (m_player) { + m_playlist->clear(); + m_player->stop(); + m_videoWidget->hide(); emit hasVideoChanged(false); } } void PhononWidget::applyVideoSize() { - if ((m_videoPlayer) && m_videoSize.isValid()) { - m_videoPlayer->setSizeHint(m_videoSize); + if ((m_videoWidget) && m_videoSize.isValid()) { + m_videoWidget->setSizeHint(m_videoSize); + } +} + +void PhononWidget::setPosition(qint64 position) +{ + if (!m_player || m_player->state() == QMediaPlayer::StoppedState) { + play(); + } else { + m_player->setPosition(position); + } +} + +void PhononWidget::positionChanged(qint64 position) +{ + if (m_player && m_player->state() != QMediaPlayer::StoppedState) { + m_seekSlider->setValue(static_cast(position)); } } +void PhononWidget::durationChanged(qint64 duration) +{ + m_seekSlider->setRange(0, static_cast(duration)); +} + + #include "phononwidget.moc"