From 46698ace2eb994bab9c19300f916ff53f4e28bf5 Mon Sep 17 00:00:00 2001 From: mek-206 Date: Sun, 29 Mar 2026 00:18:05 +0200 Subject: [PATCH] Fix issues in Media Tab and Settings Dialog for Qt6 compatibility --- src/headers/generalsettingwidget.hpp | 3 +- src/headers/mediacontrol.hpp | 8 + src/headers/mediawidget.hpp | 5 + src/headers/projectordisplayscreen.hpp | 8 +- src/headers/softprojector.hpp | 3 +- src/headers/videoinfo.hpp | 1 + src/qml/DisplayArea.qml | 213 +++++------------------- src/sources/biblesettingwidget.cpp | 111 +++++++++++-- src/sources/generalsettingwidget.cpp | 47 ++++-- src/sources/main.cpp | 5 + src/sources/mediacontrol.cpp | 61 ++++++- src/sources/mediawidget.cpp | 137 +++++++++++---- src/sources/projectordisplayscreen.cpp | 134 +++++++-------- src/sources/schedule.cpp | 2 +- src/sources/settingsdialog.cpp | 19 ++- src/sources/softprojector.cpp | 220 ++++++++++++++++--------- src/sources/songsettingwidget.cpp | 3 + src/sources/theme.cpp | 8 + src/sources/videoinfo.cpp | 1 + src/sources/videoplayerwidget.cpp | 2 +- 20 files changed, 593 insertions(+), 398 deletions(-) diff --git a/src/headers/generalsettingwidget.hpp b/src/headers/generalsettingwidget.hpp index 438c2b8..63498f5 100644 --- a/src/headers/generalsettingwidget.hpp +++ b/src/headers/generalsettingwidget.hpp @@ -40,13 +40,12 @@ class GeneralSettingWidget : public QWidget private: Ui::GeneralSettingWidget *ui; GeneralSettings mySettings; - Settings allSetings; QStringList monitors; QStringList themes; QList themeIdList; public slots: - void setSettings(GeneralSettings settings); + void setSettings(GeneralSettings sets); void updateSecondaryDisplayScreen(); GeneralSettings getSettings(); diff --git a/src/headers/mediacontrol.hpp b/src/headers/mediacontrol.hpp index 12fb9aa..a03b649 100644 --- a/src/headers/mediacontrol.hpp +++ b/src/headers/mediacontrol.hpp @@ -42,6 +42,8 @@ public slots: void updateTime(qint64 time); void setMaximumTime(qint64 maxTime); void updatePlayerState(QMediaPlayer::PlaybackState state); + QMediaPlayer::PlaybackState playbackState() const { return mPlayerState; } + bool isMuted() const; signals: void volumeChanged(int); @@ -55,10 +57,13 @@ private slots: void on_pushButtonStop_clicked(); void on_pushButtonPlayPause_clicked(); void on_pushButtonMute_toggled(bool checked); + void on_horizontalSliderTime_sliderPressed(); void on_horizontalSliderTime_sliderReleased(); void on_horizontalSliderVolume_sliderMoved(int position); +protected: + bool eventFilter(QObject *obj, QEvent *event) override; private: Ui::MediaControl *ui; @@ -69,6 +74,9 @@ private slots: QIcon mPauseIcon; QIcon mMuteIcon; QIcon mUnmuteIcon; + + bool mIsSeeking = false; + QString m_lastTimeString; }; #endif // MEDIACONTROL_HPP diff --git a/src/headers/mediawidget.hpp b/src/headers/mediawidget.hpp index 2d24f91..f7bfab4 100644 --- a/src/headers/mediawidget.hpp +++ b/src/headers/mediawidget.hpp @@ -22,6 +22,7 @@ #include #include +#include #include //#include //#include @@ -51,6 +52,7 @@ public slots: void setMediaFromSchedule(VideoInfo &v); void goLiveFromSchedule(); bool isValidMedia(); + void stopVideo(); protected: void dragEnterEvent(QDragEnterEvent *e); void dragMoveEvent(QDragMoveEvent *e); @@ -87,6 +89,7 @@ private slots: QIcon unmuteIcon; QMediaPlayer *player; + QAudioOutput *audioOutput; VideoPlayerWidget *videoWidget; MediaControl *mediaControls; @@ -99,6 +102,8 @@ private slots: QUrl currentMediaUrl; bool isReadyToPlay; + bool m_autoGoLiveAfterLoad = false; + qint64 lastReportedPosition = 0; }; #endif // MEDIAWIDGET_HPP diff --git a/src/headers/projectordisplayscreen.hpp b/src/headers/projectordisplayscreen.hpp index 6953bdb..147c453 100644 --- a/src/headers/projectordisplayscreen.hpp +++ b/src/headers/projectordisplayscreen.hpp @@ -51,7 +51,8 @@ public slots: void resetImGenSize(); void renderNotText(); - void renderPassiveText(QPixmap &back,bool useBack); + void setPassiveBackground(QPixmap pix); + void renderPassiveText(QPixmap pix,bool useBack); void renderBibleText(Verse bVerse, BibleSettings &bSets); void renderSongText(Stanza stanza, SongSettings &sSets); void renderAnnounceText(AnnounceSlide announce, TextSettings &aSets); @@ -65,8 +66,7 @@ public slots: void setVideoMuted(bool muted); void setVideoPosition(qint64 position); - void positionControls(DisplayControlsSettings & dSettings); - void setControlsVisible(bool visible); + void setAudioEnabled(bool enabled); private slots: void setBackPixmap(QPixmap p,int fillMode); // 0 = Strech, 1 = keep aspect, 2 = keep aspect by expanding @@ -101,6 +101,7 @@ private slots: private: Ui::ProjectorDisplayScreen *ui; QQuickView *dispView; + QWidget *m_videoWidget; SpImageProvider *imProvider; ImageGenerator imGen; bool backImSwitch1, textImSwitch1, backImSwitch2, textImSwitch2; @@ -110,6 +111,7 @@ private slots: // DisplayControlsSettings mySettings; QPixmap back; + QPixmap m_passiveBack; }; #endif // PROJECTORDISPLAYSCREEN_HPP diff --git a/src/headers/softprojector.hpp b/src/headers/softprojector.hpp index 3ab64e9..37d2d10 100644 --- a/src/headers/softprojector.hpp +++ b/src/headers/softprojector.hpp @@ -86,8 +86,6 @@ class SoftProjector : public QMainWindow Theme theme; Settings mySettings; - SoftProjector *softProjector; - public slots: void updateSetting(GeneralSettings &g,Theme &t, SlideShowSettings &ssets, BibleVersionSettings &bsets, BibleVersionSettings &bsets2, @@ -201,6 +199,7 @@ private slots: void on_actionPrint_triggered(); void on_actionPrintSchedule_triggered(); + void exitProjector(); void nextSlide(); void prevSlide(); diff --git a/src/headers/videoinfo.hpp b/src/headers/videoinfo.hpp index a4e8b7b..728e2ee 100644 --- a/src/headers/videoinfo.hpp +++ b/src/headers/videoinfo.hpp @@ -31,6 +31,7 @@ class VideoInfo QUrl filePath; QString fileName; int aspectRatio; + bool hasVideo; }; #endif // VIDEOINFO_HPP diff --git a/src/qml/DisplayArea.qml b/src/qml/DisplayArea.qml index 5529392..7571a55 100644 --- a/src/qml/DisplayArea.qml +++ b/src/qml/DisplayArea.qml @@ -47,18 +47,42 @@ Rectangle { signal playbackStateChanged(int state) signal playbackStopped() + property bool audioEnabled: (typeof audioEnabledInjected !== 'undefined') ? audioEnabledInjected : true + property int lastReportedPosition: 0 + property int backType: 0 // Injected from C++ (None=0, Solid=1, Pix=2, Video=3) + + AudioOutput { + id: audioOut + objectName: "audioOut" + volume: 1.0 + } + MediaPlayer { id: player objectName: "player" + videoOutput: vidOut + // Only connect the audio output if volume is > 0 and audio is enabled. + // This saves massive resources and prevents stuttering on silent background videos. + audioOutput: (audioEnabled && audioOut.volume > 0) ? audioOut : null + onSourceChanged: console.debug(player.source) onPositionChanged: { - dispArea.positionChanged(player.position) + // Throttle position reporting to about 5Hz to avoid flooding the UI thread + if (Math.abs(player.position - lastReportedPosition) > 200) { + dispArea.positionChanged(player.position) + lastReportedPosition = player.position + } } onDurationChanged: { - dispArea.durationChanged(player.duration) + var d = player.duration + if (d == 0 && player.metaData) { + // Fallback for formats that only show duration in metadata (e.g. MKV in WMF) + d = player.metaData.duration || 0 + } + dispArea.durationChanged(d) } onPlaybackStateChanged: { @@ -71,189 +95,47 @@ Rectangle { id: vidOut objectName: "vidOut" anchors.fill: parent + visible: player.hasVideo } Image { id: backImage1 objectName: "backImage1" -// anchors.fill: parent + anchors.fill: parent cache: false -// transform: Rotation -// { -// id: rotBack1 -// angle: 0 -// axis {x:0; y:0; z:0} -// origin {x:backImage1/2; y:backImage1/2} -// } } Image { id: backImage2 objectName: "backImage2" -// anchors.fill: parent + anchors.fill: parent cache: false -// transform: Rotation -// { -// id: rotBack2 -// angle: rotation1 -// axis {x:0; y:0; z:0} -// origin {x:backImage2/2; y:backImage2/2} -// } } Image { id: textImage1 objectName: "textImage1" -// anchors.fill: parent -// fillMode: Image.Stretch -// anchors.centerIn: parent + anchors.fill: parent cache: false -// transform: Rotation -// { -// id: rotText1 -// angle: 0 -// axis {x:0; y:0; z:0} -// origin {x:textImage1/2; y:textImage1/2} -// } } Image { id: textImage2 objectName: "textImage2" -// anchors.fill: parent -// fillMode: Image.Stretch -// anchors.centerIn: parent + anchors.fill: parent cache: false -// transform: Rotation -// { -// id: rotText2 -// angle: rotation2 -// axis {x:0; y:0; z:0} -// origin {x:textImage2/2; y:textImage2/2} -// } - } - - Rectangle - { - id: controls - objectName: "controls" -// opacity: 0.5 - color: "#00000000" - height: 128 - width: height*3 +20 - - x:20; y:20 - - Image - { - id: prevBtn - objectName: "prevBtn" - source: "qrc:/icons/icons/controlPrev.png" - width: parent.height; height: parent.height - - MouseArea - { - id: maPrev - anchors.fill: parent - hoverEnabled: true - onHoveredChanged: - { - if(maPrev.containsMouse) - prevBtn.source = "qrc:/icons/icons/controlPrevHovered.png" - else - prevBtn.source= "qrc:/icons/icons/controlPrev.png" - } - onPressed: - { - prevBtn.source = "qrc:/icons/icons/controlPrevPressed.png" - } - onReleased: - { - prevBtn.source = "qrc:/icons/icons/controlPrevHovered.png" - } - onClicked: - { - dispArea.prevClicked() - } - } - } - - Image - { - id: nextBtn - objectName: "nextBtn" - source: "qrc:/icons/icons/controlNext.png" - width: prevBtn.width; height: prevBtn.height - anchors.left: prevBtn.right - anchors.leftMargin: 10 - anchors.top: prevBtn.top - opacity: prevBtn.opacity - MouseArea - { - id: maNext - anchors.fill: parent - hoverEnabled: true - onHoveredChanged: - { - if(maNext.containsMouse) - nextBtn.source = "qrc:/icons/icons/controlNextHovered.png" - else - nextBtn.source= "qrc:/icons/icons/controlNext.png" - } - onPressed: - { - nextBtn.source = "qrc:/icons/icons/controlNextPressed.png" - } - onReleased: - { - nextBtn.source = "qrc:/icons/icons/controlNextHovered.png" - } - onClicked: - { - dispArea.nextClicked() - } - } - } + } - Image - { - id: exitBtn - objectName: "exitBtn" - source: "qrc:/icons/icons/controlExit.png" - width: prevBtn.width; height: prevBtn.height - anchors.left: nextBtn.right - anchors.leftMargin: 10 - anchors.top: prevBtn.top - opacity: prevBtn.opacity - MouseArea - { - id: maExit - anchors.fill: parent - hoverEnabled: true - onHoveredChanged: - { - if(maExit.containsMouse) - exitBtn.source = "qrc:/icons/icons/controlExitHovered.png" - else - exitBtn.source= "qrc:/icons/icons/controlExit.png" - } - onPressed: - { - exitBtn.source = "qrc:/icons/icons/controlExitPressed.png" - } - onReleased: - { - exitBtn.source = "qrc:/icons/icons/controlExitHovered.png" - } - onClicked: - { - dispArea.exitClicked() - } - } + // Global Key Handling for Escape + focus: true + Keys.onReleased: (event) => { + if (event.key === Qt.Key_Escape) { + dispArea.exitClicked(); + event.accepted = true; } } @@ -704,17 +586,17 @@ Rectangle { function setVideoVolume(level) { - player.volume = level + audioOut.volume = level } function setVideoMuted(toMute) { - player.muted = toMute + audioOut.muted = toMute } function setVideoPosition(position) { - player.seek(position) + player.position = position } function pauseVideo() @@ -725,18 +607,5 @@ Rectangle { } } - function positionControls(iX,iY,iSize,dOpacity) - { - controls.height = iSize; - controls.opacity = dOpacity; - controls.x = iX; - controls.y = iY; - } - - function setControlsVisible(isVisible) - { - //controls.opacity = dOpacity; - controls.visible = isVisible; - } } diff --git a/src/sources/biblesettingwidget.cpp b/src/sources/biblesettingwidget.cpp index 69059b5..3cc7619 100644 --- a/src/sources/biblesettingwidget.cpp +++ b/src/sources/biblesettingwidget.cpp @@ -44,12 +44,12 @@ void BibleSettingWidget::changeEvent(QEvent *e) } } -void BibleSettingWidget::setSettings(BibleSettings &settings, BibleSettings &settings2, BibleSettings &settings3, BibleSettings &settings4) +void BibleSettingWidget::setSettings(BibleSettings &sets, BibleSettings &sets2, BibleSettings &sets3, BibleSettings &sets4) { - mySettings = settings; - mySettings2 = settings2; - mySettings3 = settings3; - mySettings4 = settings4; + mySettings = sets; + mySettings2 = sets2; + mySettings3 = sets3; + mySettings4 = sets4; loadSettings(); } @@ -293,6 +293,7 @@ void BibleSettingWidget::getBibleVersions(BibleVersionSettings &bver, BibleVersi void BibleSettingWidget::loadSettings() { + bool oldBlock = this->blockSignals(true); // Set Effects ui->checkBoxUseShadow->setChecked(mySettings.useShadow); ui->checkBoxUseFading->setChecked(mySettings.useFading); @@ -449,22 +450,46 @@ void BibleSettingWidget::loadSettings() // Set if to use quaternary screen settings ui->groupBoxUseDisp4->setChecked(!mySettings4.useDisp1settings); on_groupBoxUseDisp4_toggled(!mySettings4.useDisp1settings); + + this->blockSignals(oldBlock); } void BibleSettingWidget::loadBibleVersions() { + // Block all bible combo signals + ui->comboBoxPrimaryBible->blockSignals(true); + ui->comboBoxSecondaryBible->blockSignals(true); + ui->comboBoxTrinaryBible->blockSignals(true); + ui->comboBoxOperatorBible->blockSignals(true); + + ui->comboBoxPrimaryBible2->blockSignals(true); + ui->comboBoxSecondaryBible2->blockSignals(true); + ui->comboBoxTrinaryBible2->blockSignals(true); + + ui->comboBoxPrimaryBible3->blockSignals(true); + ui->comboBoxSecondaryBible3->blockSignals(true); + ui->comboBoxTrinaryBible3->blockSignals(true); + + ui->comboBoxPrimaryBible4->blockSignals(true); + ui->comboBoxSecondaryBible4->blockSignals(true); + ui->comboBoxTrinaryBible4->blockSignals(true); + // Clear items if exitst bibles.clear(); bible_id_list.clear(); + bibles.detach(); + bible_id_list.detach(); // Get Bibles that exist in the database QSqlQuery sq; sq.exec("SELECT bible_name, id FROM BibleVersions"); while(sq.next()) { - bibles << sq.value(0).toString(); - bible_id_list << sq.value(1).toString(); + bibles.append(sq.value(0).toString()); + bible_id_list.append(sq.value(1).toString()); } + bibles.detach(); + bible_id_list.detach(); sq.clear(); // Fill bibles comboboxes @@ -580,17 +605,39 @@ void BibleSettingWidget::loadBibleVersions() else ui->comboBoxOperatorBible->setCurrentIndex(bible_id_list.indexOf(bversion.operatorBible)+1); updateOperatorBibleMenu(); + + // Unblock signals + ui->comboBoxPrimaryBible->blockSignals(false); + ui->comboBoxSecondaryBible->blockSignals(false); + ui->comboBoxTrinaryBible->blockSignals(false); + ui->comboBoxOperatorBible->blockSignals(false); + + ui->comboBoxPrimaryBible2->blockSignals(false); + ui->comboBoxSecondaryBible2->blockSignals(false); + ui->comboBoxTrinaryBible2->blockSignals(false); + + ui->comboBoxPrimaryBible3->blockSignals(false); + ui->comboBoxSecondaryBible3->blockSignals(false); + ui->comboBoxTrinaryBible3->blockSignals(false); + + ui->comboBoxPrimaryBible4->blockSignals(false); + ui->comboBoxSecondaryBible4->blockSignals(false); + ui->comboBoxTrinaryBible4->blockSignals(false); } void BibleSettingWidget::updateSecondaryBibleMenu() { + int primaryIndex = ui->comboBoxPrimaryBible->currentIndex(); + if (primaryIndex < 0 || primaryIndex >= bibles.count() || primaryIndex >= bible_id_list.count()) + return; + QString pbible = ui->comboBoxPrimaryBible->currentText(); QString sbible = ui->comboBoxSecondaryBible->currentText(); secondary_bibles = bibles; secondary_bibles.removeOne(pbible); secondary_id_list = bible_id_list; - secondary_id_list.removeAt(ui->comboBoxPrimaryBible->currentIndex()); + secondary_id_list.removeAt(primaryIndex); ui->comboBoxSecondaryBible->clear(); ui->comboBoxSecondaryBible->addItem(tr("None")); ui->comboBoxSecondaryBible->addItems(secondary_bibles); @@ -605,13 +652,17 @@ void BibleSettingWidget::updateSecondaryBibleMenu() void BibleSettingWidget::updateSecondaryBibleMenu2() { + int primaryIndex = ui->comboBoxPrimaryBible2->currentIndex(); + if (primaryIndex < 0 || primaryIndex >= bibles.count() || primaryIndex >= bible_id_list.count()) + return; + QString pbible = ui->comboBoxPrimaryBible2->currentText(); QString sbible = ui->comboBoxSecondaryBible2->currentText(); secondary_bibles2 = bibles; secondary_bibles2.removeOne(pbible); secondary_id_list2 = bible_id_list; - secondary_id_list2.removeAt(ui->comboBoxPrimaryBible2->currentIndex()); + secondary_id_list2.removeAt(primaryIndex); ui->comboBoxSecondaryBible2->clear(); ui->comboBoxSecondaryBible2->addItem(tr("None")); ui->comboBoxSecondaryBible2->addItems(secondary_bibles2); @@ -626,13 +677,17 @@ void BibleSettingWidget::updateSecondaryBibleMenu2() // Bible for Display 3 void BibleSettingWidget::updateSecondaryBibleMenu3() { + int primaryIndex = ui->comboBoxPrimaryBible3->currentIndex(); + if (primaryIndex < 0 || primaryIndex >= bibles.count() || primaryIndex >= bible_id_list.count()) + return; + QString pbible = ui->comboBoxPrimaryBible3->currentText(); QString sbible = ui->comboBoxSecondaryBible3->currentText(); secondary_bibles3 = bibles; secondary_bibles3.removeOne(pbible); secondary_id_list3 = bible_id_list; - secondary_id_list3.removeAt(ui->comboBoxPrimaryBible3->currentIndex()); + secondary_id_list3.removeAt(primaryIndex); ui->comboBoxSecondaryBible3->clear(); ui->comboBoxSecondaryBible3->addItem(tr("None")); ui->comboBoxSecondaryBible3->addItems(secondary_bibles3); @@ -647,13 +702,17 @@ void BibleSettingWidget::updateSecondaryBibleMenu3() // Bible for Display 4 void BibleSettingWidget::updateSecondaryBibleMenu4() { + int primaryIndex = ui->comboBoxPrimaryBible4->currentIndex(); + if (primaryIndex < 0 || primaryIndex >= bibles.count() || primaryIndex >= bible_id_list.count()) + return; + QString pbible = ui->comboBoxPrimaryBible4->currentText(); QString sbible = ui->comboBoxSecondaryBible4->currentText(); secondary_bibles4 = bibles; secondary_bibles4.removeOne(pbible); secondary_id_list4 = bible_id_list; - secondary_id_list4.removeAt(ui->comboBoxPrimaryBible4->currentIndex()); + secondary_id_list4.removeAt(primaryIndex); ui->comboBoxSecondaryBible4->clear(); ui->comboBoxSecondaryBible4->addItem(tr("None")); ui->comboBoxSecondaryBible4->addItems(secondary_bibles4); @@ -675,13 +734,17 @@ void BibleSettingWidget::updateTrinaryBibleMenu() else { ui->comboBoxTrinaryBible->setEnabled(true); + int secondaryIndex = ui->comboBoxSecondaryBible->currentIndex(); + if (secondaryIndex < 1 || secondaryIndex - 1 >= secondary_id_list.count()) + return; + QString sbible = ui->comboBoxSecondaryBible->currentText(); QString tbible = ui->comboBoxTrinaryBible->currentText(); QStringList trinary_bibles = secondary_bibles; trinary_bibles.removeOne(sbible); trinary_id_list = secondary_id_list; - trinary_id_list.removeAt(ui->comboBoxSecondaryBible->currentIndex()-1); + trinary_id_list.removeAt(secondaryIndex - 1); ui->comboBoxTrinaryBible->clear(); ui->comboBoxTrinaryBible->addItem(tr("None")); ui->comboBoxTrinaryBible->addItems(trinary_bibles); @@ -703,13 +766,17 @@ void BibleSettingWidget::updateTrinaryBibleMenu2() else { ui->comboBoxTrinaryBible2->setEnabled(true); + int secondaryIndex = ui->comboBoxSecondaryBible2->currentIndex(); + if (secondaryIndex < 1 || secondaryIndex - 1 >= secondary_id_list2.count()) + return; + QString sbible = ui->comboBoxSecondaryBible2->currentText(); QString tbible = ui->comboBoxTrinaryBible2->currentText(); QStringList trinary_bibles = secondary_bibles2; trinary_bibles.removeOne(sbible); trinary_id_list2 = secondary_id_list2; - trinary_id_list2.removeAt(ui->comboBoxSecondaryBible2->currentIndex()-1); + trinary_id_list2.removeAt(secondaryIndex - 1); ui->comboBoxTrinaryBible2->clear(); ui->comboBoxTrinaryBible2->addItem(tr("None")); ui->comboBoxTrinaryBible2->addItems(trinary_bibles); @@ -731,13 +798,17 @@ void BibleSettingWidget::updateTrinaryBibleMenu3() else { ui->comboBoxTrinaryBible3->setEnabled(true); + int secondaryIndex = ui->comboBoxSecondaryBible3->currentIndex(); + if (secondaryIndex < 1 || secondaryIndex - 1 >= secondary_id_list3.count()) + return; + QString sbible = ui->comboBoxSecondaryBible3->currentText(); QString tbible = ui->comboBoxTrinaryBible3->currentText(); QStringList trinary_bibles = secondary_bibles3; trinary_bibles.removeOne(sbible); trinary_id_list3 = secondary_id_list3; - trinary_id_list3.removeAt(ui->comboBoxSecondaryBible3->currentIndex()-1); + trinary_id_list3.removeAt(secondaryIndex - 1); ui->comboBoxTrinaryBible3->clear(); ui->comboBoxTrinaryBible3->addItem(tr("None")); ui->comboBoxTrinaryBible3->addItems(trinary_bibles); @@ -759,13 +830,17 @@ void BibleSettingWidget::updateTrinaryBibleMenu4() else { ui->comboBoxTrinaryBible4->setEnabled(true); + int secondaryIndex = ui->comboBoxSecondaryBible4->currentIndex(); + if (secondaryIndex < 1 || secondaryIndex - 1 >= secondary_id_list4.count()) + return; + QString sbible = ui->comboBoxSecondaryBible4->currentText(); QString tbible = ui->comboBoxTrinaryBible4->currentText(); QStringList trinary_bibles = secondary_bibles4; trinary_bibles.removeOne(sbible); trinary_id_list4 = secondary_id_list4; - trinary_id_list4.removeAt(ui->comboBoxSecondaryBible4->currentIndex()-1); + trinary_id_list4.removeAt(secondaryIndex - 1); ui->comboBoxTrinaryBible4->clear(); ui->comboBoxTrinaryBible4->addItem(tr("None")); ui->comboBoxTrinaryBible4->addItems(trinary_bibles); @@ -779,13 +854,17 @@ void BibleSettingWidget::updateTrinaryBibleMenu4() void BibleSettingWidget::updateOperatorBibleMenu() { + int primaryIndex = ui->comboBoxPrimaryBible->currentIndex(); + if (primaryIndex < 0 || primaryIndex >= bibles.count() || primaryIndex >= bible_id_list.count()) + return; + QString pbible = ui->comboBoxPrimaryBible->currentText(); QString obible = ui->comboBoxOperatorBible->currentText(); QStringList operator_bibles = bibles; operator_bibles.removeOne(pbible); operator_id_list = bible_id_list; - operator_id_list.removeAt(ui->comboBoxPrimaryBible->currentIndex()); + operator_id_list.removeAt(primaryIndex); ui->comboBoxOperatorBible->clear(); ui->comboBoxOperatorBible->addItem(tr("Same as primary Bible")); ui->comboBoxOperatorBible->addItems(operator_bibles); diff --git a/src/sources/generalsettingwidget.cpp b/src/sources/generalsettingwidget.cpp index 4fbd65e..a3de99e 100644 --- a/src/sources/generalsettingwidget.cpp +++ b/src/sources/generalsettingwidget.cpp @@ -44,17 +44,22 @@ void GeneralSettingWidget::changeEvent(QEvent *e) } } -void GeneralSettingWidget::setSettings(GeneralSettings settings) +void GeneralSettingWidget::setSettings(GeneralSettings sets) { - mySettings = settings; + mySettings = sets; loadSettings(); } void GeneralSettingWidget::loadSettings() { - ui->checkBoxDisplayOnTop->setChecked(mySettings.displayIsOnTop); - QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",QSettings::NativeFormat); - if(settings.value("SoftProjectorUseLightTheme")==0) + // Block signals to prevent recursive calls or premature updates while loading + bool oldBlock = this->blockSignals(true); + ui->comboBoxDisplayScreen->blockSignals(true); + ui->comboBoxDisplayScreen_2->blockSignals(true); + ui->comboBoxDisplayScreen_3->blockSignals(true); + ui->comboBoxDisplayScreen_4->blockSignals(true); + QSettings regSettings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", QSettings::NativeFormat); + if(regSettings.value("SoftProjectorUseLightTheme")==0) ui->checkBoxUseDarkTheme->setChecked(true); else ui->checkBoxUseDarkTheme->setChecked(false); @@ -67,15 +72,19 @@ void GeneralSettingWidget::loadSettings() // Get display screen infomration monitors.clear(); + monitors.detach(); // Explicitly detach to ensure it's mutable - int screen_count = QApplication::screens().count(); + QList screens = QGuiApplication::screens(); + int screen_count = screens.count(); int i = 1; ui->comboBoxDisplayScreen->clear(); - for(QScreen * s: QApplication::screens()) + for(int j = 0; j < screens.count(); ++j) { - monitors << QString("%1 - %2x%3").arg(i).arg(s->geometry().width()).arg(s->geometry().height()); + QScreen *s = screens.at(j); + monitors.append(QString("%1 - %2x%3").arg(i).arg(s->geometry().width()).arg(s->geometry().height())); ++i; } + monitors.detach(); if(screen_count>1) { @@ -157,6 +166,13 @@ void GeneralSettingWidget::loadSettings() ui->comboBoxControlsAlignV->setCurrentIndex(mySettings.displayControls.alignmentV); ui->comboBoxControlsAlignH->setCurrentIndex(mySettings.displayControls.alignmentH); ui->horizontalSliderOpacity->setValue(mySettings.displayControls.opacity*100); + + // Unblock signals + ui->comboBoxDisplayScreen->blockSignals(false); + ui->comboBoxDisplayScreen_2->blockSignals(false); + ui->comboBoxDisplayScreen_3->blockSignals(false); + ui->comboBoxDisplayScreen_4->blockSignals(false); + this->blockSignals(oldBlock); } void GeneralSettingWidget::loadThemes() @@ -165,11 +181,15 @@ void GeneralSettingWidget::loadThemes() sq.exec("SELECT id, name FROM Themes"); themeIdList.clear(); themes.clear(); + themeIdList.detach(); + themes.detach(); while(sq.next()) { - themeIdList<< sq.value(0).toInt(); - themes<comboBoxTheme->clear(); ui->comboBoxTheme->addItems(themes); } @@ -215,9 +235,11 @@ void GeneralSettingWidget::updateSecondaryDisplayScreen() QString ds3 = ui->comboBoxDisplayScreen_3->currentText(); QString ds4 = ui->comboBoxDisplayScreen_4->currentText(); QStringList monitors2 = monitors; + monitors2.detach(); //Display 2 (ds2) monitors2.removeOne(ds1); + ui->comboBoxDisplayScreen_2->blockSignals(true); ui->comboBoxDisplayScreen_2->clear(); ui->comboBoxDisplayScreen_2->addItem(tr("None")); ui->comboBoxDisplayScreen_2->addItems(monitors2); @@ -230,11 +252,13 @@ void GeneralSettingWidget::updateSecondaryDisplayScreen() ui->comboBoxDisplayScreen_2->setCurrentIndex(0); emit setDisp2Use(false); } + ui->comboBoxDisplayScreen_2->blockSignals(false); // Display 3 (ds3) monitors2.removeOne(ds1); monitors2.removeOne(ds2); + ui->comboBoxDisplayScreen_3->blockSignals(true); ui->comboBoxDisplayScreen_3->clear(); ui->comboBoxDisplayScreen_3->addItem(tr("None")); ui->comboBoxDisplayScreen_3->addItems(monitors2); @@ -247,12 +271,14 @@ void GeneralSettingWidget::updateSecondaryDisplayScreen() ui->comboBoxDisplayScreen_3->setCurrentIndex(0); emit setDisp3Use(false); } + ui->comboBoxDisplayScreen_3->blockSignals(false); // Display 4 (ds4) monitors2.removeOne(ds1); monitors2.removeOne(ds2); monitors2.removeOne(ds3); + ui->comboBoxDisplayScreen_4->blockSignals(true); ui->comboBoxDisplayScreen_4->clear(); ui->comboBoxDisplayScreen_4->addItem(tr("None")); ui->comboBoxDisplayScreen_4->addItems(monitors2); @@ -265,6 +291,7 @@ void GeneralSettingWidget::updateSecondaryDisplayScreen() ui->comboBoxDisplayScreen_4->setCurrentIndex(0); emit setDisp4Use(false); } + ui->comboBoxDisplayScreen_4->blockSignals(false); } void GeneralSettingWidget::on_comboBoxDisplayScreen_activated(const QString &arg1) diff --git a/src/sources/main.cpp b/src/sources/main.cpp index ae782fb..bc2377f 100644 --- a/src/sources/main.cpp +++ b/src/sources/main.cpp @@ -109,6 +109,11 @@ bool connect(QString database_file) int main(int argc, char *argv[]) { +#ifdef Q_OS_WIN + // Force the native Windows Media Foundation backend. + // This is required for smooth WMV playback and audio sync. + qputenv("QT_MEDIA_BACKEND", "windows"); +#endif QApplication a(argc, argv); a.setApplicationName("SoftProjector"); diff --git a/src/sources/mediacontrol.cpp b/src/sources/mediacontrol.cpp index 036c72a..988e467 100644 --- a/src/sources/mediacontrol.cpp +++ b/src/sources/mediacontrol.cpp @@ -19,6 +19,14 @@ #include "../headers/mediacontrol.hpp" #include "ui_mediacontrol.h" +#include +#include +#include + +bool MediaControl::isMuted() const +{ + return ui->pushButtonMute->isChecked(); +} MediaControl::MediaControl(QWidget *parent) : QWidget(parent), @@ -34,6 +42,8 @@ MediaControl::MediaControl(QWidget *parent) : mPauseIcon = QIcon(":icons/icons/pause.png"); mMuteIcon = QIcon(":icons/icons/speakerMute.png"); mUnmuteIcon = QIcon(":icons/icons/speaker.png"); + + ui->horizontalSliderTime->installEventFilter(this); } MediaControl::~MediaControl() @@ -48,7 +58,12 @@ void MediaControl::setVolume(int level) void MediaControl::updateTime(qint64 time) { - ui->horizontalSliderTime->setValue(time/1000); + if (mIsSeeking) return; // don't fight the user's drag + + // Throttle slider updates to avoid UI stuttering with millisecond resolution + if (qAbs(ui->horizontalSliderTime->value() - time) > 100 || time == 0) { + ui->horizontalSliderTime->setValue(time); + } QString timeString; if (time || mDuration) @@ -77,13 +92,17 @@ void MediaControl::updateTime(qint64 time) timeString += " / " + stopTime.toString(timeFormat); } } - ui->labelTime->setText(timeString); + + if (m_lastTimeString != timeString) { + ui->labelTime->setText(timeString); + m_lastTimeString = timeString; + } } void MediaControl::setMaximumTime(qint64 maxTime) { mDuration = maxTime; - ui->horizontalSliderTime->setMaximum(maxTime/1000); + ui->horizontalSliderTime->setMaximum(maxTime); } void MediaControl::updatePlayerState(QMediaPlayer::PlaybackState state) @@ -138,9 +157,18 @@ void MediaControl::on_pushButtonMute_toggled(bool checked) } } +void MediaControl::on_horizontalSliderTime_sliderPressed() +{ + mIsSeeking = true; // block position updates from the player +} + void MediaControl::on_horizontalSliderTime_sliderReleased() { - emit timeChanged(ui->horizontalSliderTime->value() * 1000); + // Delayed release of mIsSeeking to avoid jump-back/stuttering UI. + // Give the player time (300ms) to update its position before resume UI synchronization. + QTimer::singleShot(300, this, [this](){ mIsSeeking = false; }); + + emit timeChanged(ui->horizontalSliderTime->value()); } void MediaControl::on_horizontalSliderVolume_sliderMoved(int position) @@ -148,3 +176,28 @@ void MediaControl::on_horizontalSliderVolume_sliderMoved(int position) emit volumeChanged(position); } +bool MediaControl::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == ui->horizontalSliderTime && event->type() == QEvent::MouseButtonPress) + { + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->button() == Qt::LeftButton) + { + // Calculate new value based on click position + int newVal = QStyle::sliderValueFromPosition(ui->horizontalSliderTime->minimum(), + ui->horizontalSliderTime->maximum(), + mouseEvent->position().toPoint().x(), + ui->horizontalSliderTime->width()); + + mIsSeeking = true; + ui->horizontalSliderTime->setValue(newVal); + emit timeChanged((qint64)newVal); + + // Allow 500ms for seeking to settle before resuming UI position synchronization + QTimer::singleShot(500, this, [this](){ mIsSeeking = false; }); + return true; // event handled + } + } + return QWidget::eventFilter(obj, event); +} + diff --git a/src/sources/mediawidget.cpp b/src/sources/mediawidget.cpp index 1e5383d..00be9ee 100644 --- a/src/sources/mediawidget.cpp +++ b/src/sources/mediawidget.cpp @@ -17,7 +17,9 @@ // ***************************************************************************/ +#include #include +#include #include "../headers/mediawidget.hpp" #include "ui_mediawidget.h" @@ -37,28 +39,58 @@ MediaWidget::MediaWidget(QWidget *parent) : mediaControls = new MediaControl(this); ui->horizontalLayoutControls->addWidget(mediaControls); + // Qt6 audio output (replaces QMediaPlayer::setVolume/setMuted) + audioOutput = new QAudioOutput(this); + player->setAudioOutput(audioOutput); + connect(player, SIGNAL(metaDataChanged()), this, SLOT(updateInfo())); connect(player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(statusChanged(QMediaPlayer::MediaStatus))); -// connect(player, SIGNAL(bufferStatusChanged(int)), this, SLOT(bufferingProgress(int))); - connect(player, SIGNAL(videoAvailableChanged(bool)), this, SLOT(hasVideoChanged(bool))); -// connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(displayErrorMessage())); - + // Qt6: videoAvailableChanged removed; use hasVideoChanged triggered from statusChanged + connect(player, &QMediaPlayer::sourceChanged, this, [this](const QUrl &) { + // When source changes, reset video visibility until media loads + hasVideoChanged(false); + }); - connect(mediaControls, SIGNAL(muted(bool)),player,SLOT(setMuted(bool))); + connect(mediaControls, &MediaControl::muted, audioOutput, &QAudioOutput::setMuted); connect(mediaControls, SIGNAL(play()),player,SLOT(play())); connect(mediaControls, SIGNAL(pause()),player,SLOT(pause())); connect(mediaControls, SIGNAL(stop()),player,SLOT(stop())); - connect(mediaControls, SIGNAL(timeChanged(qint64)),player,SLOT(setPosition(qint64))); - connect(mediaControls, SIGNAL(volumeChanged(int)),player,SLOT(setVolume(int))); - - - connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), mediaControls, SLOT(updatePlayerState(QMediaPlayer::State))); - connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(displayErrorMessage())); + connect(mediaControls, &MediaControl::timeChanged, this, [this](qint64 pos) { + // Temporarily mute to hide stuttering sound during seek + bool wasMuted = audioOutput->isMuted(); + audioOutput->setMuted(true); + + player->setPosition(pos); + + // Restore muted state after a short delay + QTimer::singleShot(300, this, [this, wasMuted]() { + audioOutput->setMuted(wasMuted); + }); + }); + connect(mediaControls, &MediaControl::volumeChanged, this, [this](int v) { + float fv = v / 100.0f; + audioOutput->setVolume(fv); + // Dynamically detach audio output if volume is 0 to save resources and avoid sync stuttering + player->setAudioOutput(fv > 0 ? audioOutput : nullptr); + }); + + // Qt6: stateChanged(QMediaPlayer::State) → playbackStateChanged(QMediaPlayer::PlaybackState) + connect(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)), + mediaControls, SLOT(updatePlayerState(QMediaPlayer::PlaybackState))); + connect(player, SIGNAL(errorOccurred(QMediaPlayer::Error, const QString&)), + this, SLOT(displayErrorMessage())); connect(player, SIGNAL(durationChanged(qint64)), mediaControls, SLOT(setMaximumTime(qint64))); - connect(player, SIGNAL(positionChanged(qint64)), mediaControls, SLOT(updateTime(qint64))); - connect(player, SIGNAL(volumeChanged(int)),mediaControls,SLOT(setVolume(int))); - videoWidget = new VideoPlayerWidget(this); + connect(player, &QMediaPlayer::positionChanged, this, [this](qint64 pos) { + if (qAbs(pos - lastReportedPosition) > 200 || pos == 0) { + mediaControls->updateTime(pos); + lastReportedPosition = pos; + } + }); + // Sync initial volume to audioOutput + audioOutput->setVolume(1.0f); + + videoWidget = new VideoPlayerWidget(this); player->setVideoOutput(videoWidget); mediaControls->setVolume(100); @@ -100,6 +132,7 @@ MediaWidget::MediaWidget(QWidget *parent) : MediaWidget::~MediaWidget() { delete player; + delete audioOutput; delete videoWidget; delete mediaControls; // delete timeSlider; @@ -138,8 +171,14 @@ void MediaWidget::statusChanged(QMediaPlayer::MediaStatus status) switch (status) { case QMediaPlayer::BufferingMedia: case QMediaPlayer::LoadingMedia: - ui->labelInfo->setText(QString("
%1") - .arg(tr("Loading..."))); + // Delay showing "Loading..." to avoid flickering/stuttering during short buffer events + QTimer::singleShot(500, this, [this]() { + if (player->mediaStatus() == QMediaPlayer::BufferingMedia || + player->mediaStatus() == QMediaPlayer::LoadingMedia) { + ui->labelInfo->setText(QString("
%1") + .arg(tr("Loading..."))); + } + }); break; case QMediaPlayer::StalledMedia: ui->labelInfo->setText(QString("
%1") @@ -151,6 +190,21 @@ void MediaWidget::statusChanged(QMediaPlayer::MediaStatus status) case QMediaPlayer::LoadedMedia: case QMediaPlayer::BufferedMedia: isReadyToPlay = true; + updateInfo(); // Clear "Loading..." and show metadata + // Qt6: videoAvailableChanged removed — check if player has a video track + hasVideoChanged(player->hasVideo()); + + if (m_autoGoLiveAfterLoad) { + m_autoGoLiveAfterLoad = false; + qDebug() << "MediaWidget: Auto-projection flag detected after load. Going live."; + goLiveFromSchedule(); + } + break; + case QMediaPlayer::NoMedia: + case QMediaPlayer::EndOfMedia: + hasVideoChanged(false); + break; + default: break; } } @@ -248,6 +302,15 @@ void MediaWidget::updateInfo() QString font = ""; QString fName = player->source().fileName(); + qint64 dur = player->duration(); + if (dur == 0) { + // Fallback for types (like MKV in WMF) that report duration in metadata but not directly. + dur = player->metaData().value(QMediaMetaData::Duration).toLongLong(); + } + if (dur > 0) { + mediaControls->setMaximumTime(dur); + } + QMediaMetaData md = player->metaData(); QString tAlbum = md.stringValue(QMediaMetaData::AlbumTitle); QString tTitle = md.stringValue(QMediaMetaData::Title); @@ -312,10 +375,13 @@ void MediaWidget::hasVideoChanged(bool bHasVideo) qDebug()<<"hasVideoChanged"<isFullScreen()) videoWidget->setFullScreen(false); + ui->labelInfo->setVisible(!bHasVideo); - ui->pushButtonGoLive->setEnabled(bHasVideo); + + // Enable "Go Live" if we have either video or audio content loaded + ui->pushButtonGoLive->setEnabled(bHasVideo || player->hasAudio()); + videoWidget->setVisible(bHasVideo); -// isReadyToPlay = true; } void MediaWidget::prepareForProjection() @@ -326,6 +392,16 @@ void MediaWidget::prepareForProjection() // v.filePath = mediaFilePaths.at(ui->listWidgetMediaFiles->currentRow()); v.fileName = currentMediaUrl.fileName(); v.filePath = currentMediaUrl.toString(); + v.hasVideo = player->hasVideo(); + + // Heuristic: If it's a known audio-only extension, force hasVideo to false + // to ensure passive background is shown even if the file has embedded album art (which Qt sees as a video track). + QString ext = v.fileName.split('.').last().toLower(); + if (v.hasVideo && (ext == "mp3" || ext == "m4a" || ext == "wav" || ext == "aac" || ext == "flac" || ext == "ogg" || ext == "wma")) { + qDebug() << "MediaWidget: Forced hasVideo to false for audio file extension:" << ext; + v.hasVideo = false; + } + emit toProjector(v); } @@ -406,17 +482,13 @@ void MediaWidget::setMediaFromSchedule(VideoInfo &v) void MediaWidget::goLiveFromSchedule() { - while (!isReadyToPlay) - { - int ms = 1000; -#ifdef Q_OS_WIN - Sleep(uint(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, nullptr); -#endif + if (!isReadyToPlay) { + qDebug() << "MediaWidget: Media not ready yet, setting auto-projection flag."; + m_autoGoLiveAfterLoad = true; + return; } - qDebug()<isVisible()<pushButtonGoLive->isEnabled(); + + qDebug() << "MediaWidget: goLiveFromSchedule, isReadyToPlay:" << isReadyToPlay; if(ui->pushButtonGoLive->isEnabled()) { qDebug()<<"Go Live is Enabled"; @@ -436,3 +508,12 @@ bool MediaWidget::isValidMedia() else return false; } + +void MediaWidget::stopVideo() +{ + qDebug() << "MediaWidget: stopping video."; + m_autoGoLiveAfterLoad = false; + player->stop(); + hasVideoChanged(false); + ui->labelInfo->setText(tr("
Stopped
")); +} diff --git a/src/sources/projectordisplayscreen.cpp b/src/sources/projectordisplayscreen.cpp index b3ef013..06216cd 100644 --- a/src/sources/projectordisplayscreen.cpp +++ b/src/sources/projectordisplayscreen.cpp @@ -17,7 +17,6 @@ // ***************************************************************************/ -#include "../3rdparty/headers/qmediaplaylist.h" #include "../headers/projectordisplayscreen.hpp" #include "ui_projectordisplayscreen.h" @@ -30,9 +29,9 @@ ProjectorDisplayScreen::ProjectorDisplayScreen(QWidget *parent) : dispView = new QQuickView; imProvider = new SpImageProvider; dispView->engine()->addImageProvider(QLatin1String("improvider"),imProvider); - QWidget *w = QWidget::createWindowContainer(dispView,this); + m_videoWidget = QWidget::createWindowContainer(dispView,this); dispView->setSource(QUrl("qrc:/qml/qml/DisplayArea.qml")); - ui->verticalLayout->addWidget(w); + ui->verticalLayout->addWidget(m_videoWidget); // Create Display object for retriaving signals back from the display screen QObject *dispObj = dispView->rootObject(); @@ -48,6 +47,7 @@ ProjectorDisplayScreen::ProjectorDisplayScreen(QWidget *parent) : backImSwitch1 = backImSwitch2 = textImSwitch1 = textImSwitch2 = false; back1to2 = text1to2 = isNewBack = true; + tranType = TR_FADE; m_color.setRgb(0,0,0,0);// = QColor(QColor::black()); } @@ -178,7 +178,7 @@ void ProjectorDisplayScreen::setBackVideo(QString path) setVideoSource(item,path); item->setProperty("volume",0.0); - item->setProperty("loops",QMediaPlaylist::Loop); + item->setProperty("loops", -1); // Qt6 QML: MediaPlayer.Infinite = loop forever item2->setProperty("fillMode",Qt::IgnoreAspectRatio); } @@ -195,6 +195,8 @@ void ProjectorDisplayScreen::setVideoSource(QObject* playerObject, QUrl path) void ProjectorDisplayScreen::updateScreen() { QObject *root = dispView->rootObject(); + root->setProperty("backType", backType); // Inject current background type (None/Image/Video) + QMetaObject::invokeMethod(root,"stopTransitions"); // QString tranType = "seq"; @@ -241,11 +243,13 @@ void ProjectorDisplayScreen::exitSlideClicked() void ProjectorDisplayScreen::nextSlideClicked() { + qDebug() << "PDS: nextSlideClicked() from QML."; emit nextSlide(); } void ProjectorDisplayScreen::prevSlideClicked() { + qDebug() << "PDS: prevSlideClicked() from QML."; emit prevSlide(); } @@ -294,19 +298,28 @@ void ProjectorDisplayScreen::keyReleaseEvent(QKeyEvent *event) QWidget::keyReleaseEvent(event); } +void ProjectorDisplayScreen::setPassiveBackground(QPixmap pix) +{ + m_passiveBack = pix; +} + void ProjectorDisplayScreen::renderNotText() { setTextPixmap(imGen.generateEmptyImage()); updateScreen(); } -void ProjectorDisplayScreen::renderPassiveText(QPixmap &back, bool useBack) +void ProjectorDisplayScreen::renderPassiveText(QPixmap pix, bool useBack) { + qDebug() << "PDS: renderPassiveText called. UseBack:" << useBack << "PixSize:" << pix.size(); + setPassiveBackground(pix); + + tranType = TR_FADE; setTextPixmap(imGen.generateEmptyImage()); if(useBack) { backType = B_PICTURE; - setBackPixmap(back,0); + setBackPixmap(m_passiveBack,0); } else { @@ -420,26 +433,56 @@ void ProjectorDisplayScreen::renderSlideShow(QPixmap slide, SlideShowSettings &s { tranType = TR_FADE; + // Use passive background if available, otherwise solid color + if (!m_passiveBack.isNull()) { + setBackPixmap(m_passiveBack, 0); // 0 = Stretch + } else { + setBackPixmap(imGen.generateColorImage(m_color), 0); + } + + // Generate foreground with the slide centered on top + QPixmap foreground = imGen.generateEmptyImage(); + QPainter painter(&foreground); + bool expand; - if(slide.width()rootObject(); QObject *item = root->findChild("player"); QObject *item2 = root->findChild("vidOut"); @@ -447,8 +490,11 @@ void ProjectorDisplayScreen::renderVideo(VideoInfo videoDetails) setVideoSource(item,videoDetails.filePath); item->setProperty("volume",1.0); - item->setProperty("loops",QMediaPlaylist::CurrentItemOnce); + item->setProperty("loops", 1); // Qt6 QML: play once item2->setProperty("fillMode",Qt::KeepAspectRatio); + + // Ensure display view has focus to capture global keys like Escape + m_videoWidget->setFocus(Qt::ActiveWindowFocusReason); updateScreen(); } @@ -486,7 +532,7 @@ void ProjectorDisplayScreen::setVideoVolume(int level) void ProjectorDisplayScreen::setVideoMuted(bool muted) { QObject *root = dispView->rootObject(); - QMetaObject::invokeMethod(root,"setVideoVolume",Q_ARG(QVariant,(!muted))); + QMetaObject::invokeMethod(root,"setVideoMuted",Q_ARG(QVariant,muted)); } void ProjectorDisplayScreen::setVideoPosition(qint64 position) @@ -495,59 +541,7 @@ void ProjectorDisplayScreen::setVideoPosition(qint64 position) QMetaObject::invokeMethod(root,"setVideoPosition",Q_ARG(QVariant,position)); } -void ProjectorDisplayScreen::positionControls(DisplayControlsSettings &dSettings) -{ - //mySettings = dSettings; - - // set icon size - int buttonSize(dSettings.buttonSize); - if(buttonSize == 0) - buttonSize = 16; - else if(buttonSize == 1) - buttonSize = 24; - else if(buttonSize == 2) - buttonSize = 32; - else if(buttonSize == 3) - buttonSize = 48; - else if(buttonSize == 4) - buttonSize = 64; - else if(buttonSize == 5) - buttonSize = 96; - else - buttonSize = 48; - - - // calculate button position - int y(this->height()), x(this->width()), margin(20); - - // calculate y position - if(dSettings.alignmentV==0)//top - y = margin; - else if(dSettings.alignmentV==1)//middle - y = (y-buttonSize)/2; - else if(dSettings.alignmentV==2)//buttom - y = y-buttonSize-margin; - else - y = y-buttonSize-margin; - - // calculate x position - int xt((buttonSize*3)+20); //total width of the button group - if(dSettings.alignmentH==0) - x = margin; - else if(dSettings.alignmentH==1) - x = (x-xt)/2; - else if (dSettings.alignmentH==2) - x = x-xt-margin; - else - x = (x-xt)/2; - - QObject *root = dispView->rootObject(); - QMetaObject::invokeMethod(root,"positionControls",Q_ARG(QVariant,x),Q_ARG(QVariant,y),Q_ARG(QVariant,buttonSize),Q_ARG(QVariant,dSettings.opacity)); - -} - -void ProjectorDisplayScreen::setControlsVisible(bool visible) +void ProjectorDisplayScreen::setAudioEnabled(bool enabled) { - QObject *root = dispView->rootObject(); - QMetaObject::invokeMethod(root,"setControlsVisible",Q_ARG(QVariant,visible)); + dispView->rootContext()->setContextProperty("audioEnabledInjected", enabled); } diff --git a/src/sources/schedule.cpp b/src/sources/schedule.cpp index cee2bb1..4fea4cd 100644 --- a/src/sources/schedule.cpp +++ b/src/sources/schedule.cpp @@ -56,7 +56,7 @@ Schedule::Schedule(VideoInfo &m) scid = -1; stype = "media"; name = m.fileName; - icon = QIcon(":/icons/icons/video.png"); + icon = QIcon(m.hasVideo ? ":/icons/icons/video.png" : ":/icons/icons/speaker.png"); media = m; } diff --git a/src/sources/settingsdialog.cpp b/src/sources/settingsdialog.cpp index 5d1b429..c920941 100644 --- a/src/sources/settingsdialog.cpp +++ b/src/sources/settingsdialog.cpp @@ -65,6 +65,7 @@ void SettingsDialog::loadSettings(GeneralSettings &sets, Theme &thm, SlideShowSe BibleVersionSettings &bsets, BibleVersionSettings &bsets2, BibleVersionSettings &bsets3, BibleVersionSettings &bsets4) { + bool oldBlock = this->blockSignals(true); gsettings = sets; theme = thm; bsettings = bsets; @@ -85,18 +86,12 @@ void SettingsDialog::loadSettings(GeneralSettings &sets, Theme &thm, SlideShowSe bibleSettingswidget->setBibleVersions(bsettings,bsettings2,bsettings3,bsettings4); pictureSettingWidget->setSettings(ssettings); setThemes(); + this->blockSignals(oldBlock); } SettingsDialog::~SettingsDialog() { delete ui; - - delete generalSettingswidget; - delete passiveSettingwidget; - delete bibleSettingswidget; - delete songSettingswidget; - delete announcementSettingswidget; - delete btnOk; delete btnCancel; delete btnApply; @@ -200,10 +195,20 @@ void SettingsDialog::getThemes() void SettingsDialog::setThemes() { + bool old1 = passiveSettingwidget->blockSignals(true); + bool old2 = bibleSettingswidget->blockSignals(true); + bool old3 = songSettingswidget->blockSignals(true); + bool old4 = announcementSettingswidget->blockSignals(true); + passiveSettingwidget->setSetings(theme.passive, theme.passive2, theme.passive3, theme.passive4); bibleSettingswidget->setSettings(theme.bible, theme.bible2, theme.bible3, theme.bible4); songSettingswidget->setSettings(theme.song, theme.song2, theme.song3, theme.song4); announcementSettingswidget->setSettings(theme.announce, theme.announce2, theme.announce3, theme.announce4); + + passiveSettingwidget->blockSignals(old1); + bibleSettingswidget->blockSignals(old2); + songSettingswidget->blockSignals(old3); + announcementSettingswidget->blockSignals(old4); } void SettingsDialog::changeTheme(int theme_id) diff --git a/src/sources/softprojector.cpp b/src/sources/softprojector.cpp index a2f33fc..9c9d9f2 100644 --- a/src/sources/softprojector.cpp +++ b/src/sources/softprojector.cpp @@ -47,6 +47,10 @@ SoftProjector::SoftProjector(QWidget *parent) pds2 = new ProjectorDisplayScreen(); //for future pds3 = new ProjectorDisplayScreen(); //for future pds4 = new ProjectorDisplayScreen(); //for future + // Disable audio for additional display screens to avoid resource contention and audio stuttering + pds2->setAudioEnabled(false); + pds3->setAudioEnabled(false); + pds4->setAudioEnabled(false); // Don't worry, we'll move it later bibleWidget = new BibleWidget; @@ -105,9 +109,21 @@ SoftProjector::SoftProjector(QWidget *parent) connect(manageDialog, SIGNAL(setMainArrowCursor()), this, SLOT(setArrowCursor())); connect(manageDialog, SIGNAL(setMainWaitCursor()), this, SLOT(setWaitCursor())); connect(languageGroup, SIGNAL(triggered(QAction*)), this, SLOT(switchLanguage(QAction*))); - connect(pds1,SIGNAL(exitSlide()),this,SLOT(on_actionHide_triggered())); + connect(pds1,SIGNAL(exitSlide()),this,SLOT(exitProjector())); connect(pds1,SIGNAL(nextSlide()),this,SLOT(nextSlide())); connect(pds1,SIGNAL(prevSlide()),this,SLOT(prevSlide())); + + connect(pds2,SIGNAL(exitSlide()),this,SLOT(exitProjector())); + connect(pds2,SIGNAL(nextSlide()),this,SLOT(nextSlide())); + connect(pds2,SIGNAL(prevSlide()),this,SLOT(prevSlide())); + + connect(pds3,SIGNAL(exitSlide()),this,SLOT(exitProjector())); + connect(pds3,SIGNAL(nextSlide()),this,SLOT(nextSlide())); + connect(pds3,SIGNAL(prevSlide()),this,SLOT(prevSlide())); + + connect(pds4,SIGNAL(exitSlide()),this,SLOT(exitProjector())); + connect(pds4,SIGNAL(nextSlide()),this,SLOT(nextSlide())); + connect(pds4,SIGNAL(prevSlide()),this,SLOT(prevSlide())); connect(settingsDialog,SIGNAL(updateSettings(GeneralSettings&,Theme&,SlideShowSettings&, BibleVersionSettings&,BibleVersionSettings&, BibleVersionSettings&,BibleVersionSettings&)), @@ -176,8 +192,8 @@ SoftProjector::SoftProjector(QWidget *parent) mediaControls,SLOT(updateTime(qint64))); connect(pds1,SIGNAL(videoDurationChanged(qint64)), mediaControls,SLOT(setMaximumTime(qint64))); - connect(pds1,SIGNAL(videoPlaybackStateChanged(QMediaPlayer::State)), - mediaControls,SLOT(updatePlayerState(QMediaPlayer::State))); + connect(pds1,SIGNAL(videoPlaybackStateChanged(QMediaPlayer::PlaybackState)), + mediaControls,SLOT(updatePlayerState(QMediaPlayer::PlaybackState))); connect(pds1,SIGNAL(videoStopped()),this,SLOT(videoStopped())); connect(mediaControls,SIGNAL(play()),this,SLOT(playVideo())); connect(mediaControls,SIGNAL(pause()),this,SLOT(pauseVideo())); @@ -225,58 +241,47 @@ void SoftProjector::positionDisplayWindow() pds3->setWindowFlags(Qt::WindowStaysOnTopHint); pds4->setWindowFlags(Qt::WindowStaysOnTopHint); } - else - { - // pds1->setWindowFlags(Qt::WindowStaysOnBottomHint); // Do not show always on top - // pds2->setWindowFlags(Qt::WindowStaysOnBottomHint); // Do not show always on top - // pds3->setWindowFlags(Qt::WindowStaysOnBottomHint); // Do not show always on top - // pds4->setWindowFlags(Qt::WindowStaysOnBottomHint); // Do not show always on top - } - qDebug()<< "Screen Count: " << QApplication::primaryScreen()->virtualSiblings().count(); + QList screens = QGuiApplication::screens(); + int screenCount = screens.count(); + qDebug() << "Screen Count: " << screenCount; - if(QApplication::primaryScreen()->virtualSiblings().count() > 1) + if(screenCount > 1) { - - // if (desktop->isVirtualDesktop()) + // Display Screen 1 + int s1 = mySettings.general.displayScreen; + if(s1 >= 0 && s1 < screenCount) + { + pds1->setGeometry(screens.at(s1)->geometry()); + } + else { - // Move the display widget to screen 1 (secondary screen): - pds1->setGeometry(QApplication::primaryScreen()->virtualSiblings().at(mySettings.general.displayScreen)->geometry()); + // Default to screen 2 (index 1) if s1 is out of range + pds1->setGeometry(screens.at(1)->geometry()); } - pds1->setCursor(Qt::BlankCursor); //Sets a Blank Mouse to the screen + pds1->setCursor(Qt::BlankCursor); pds1->resetImGenSize(); pds1->renderPassiveText(theme.passive.backgroundPix,theme.passive.useBackground); - pds1->setControlsVisible(false); if(mySettings.general.displayOnStartUp) { pds1->showFullScreen(); - ui->actionCloseDisplay->setChecked(true); updateCloseDisplayButtons(true); - } - // check if to display secondary display screen - if(mySettings.general.displayScreen2>=0) + // Display Screen 2 + int s2 = mySettings.general.displayScreen2; + if(s2 >= 0 && s2 < screenCount) { hasDisplayScreen2 = true; - // if (desktop->isVirtualDesktop()) - { - // Move the display widget to screen 1 (secondary screen): - pds2->setGeometry(QApplication::primaryScreen()->virtualSiblings().at(mySettings.general.displayScreen2)->geometry()); - pds2->resetImGenSize(); - - } - - pds2->setCursor(Qt::BlankCursor); //Sets a Blank Mouse to the screen + pds2->setGeometry(screens.at(s2)->geometry()); + pds2->resetImGenSize(); + pds2->setCursor(Qt::BlankCursor); pds2->renderPassiveText(theme.passive2.backgroundPix,theme.passive2.useBackground); - pds2->setControlsVisible(false); if(mySettings.general.displayOnStartUp) - { pds2->showFullScreen(); - } } else { @@ -284,24 +289,17 @@ void SoftProjector::positionDisplayWindow() pds2->hide(); } - // check if to display tertiary display screen - if(mySettings.general.displayScreen3>=0) + // Display Screen 3 + int s3 = mySettings.general.displayScreen3; + if(s3 >= 0 && s3 < screenCount) { hasDisplayScreen3 = true; - // if (desktop->isVirtualDesktop()) - { - // Move the display widget to screen 1 (tertiary screen): - pds3->setGeometry(QApplication::primaryScreen()->virtualSiblings().at(mySettings.general.displayScreen3)->geometry()); - pds3->resetImGenSize(); - - } - pds3->setCursor(Qt::BlankCursor); //Sets a Blank Mouse to the screen + pds3->setGeometry(screens.at(s3)->geometry()); + pds3->resetImGenSize(); + pds3->setCursor(Qt::BlankCursor); pds3->renderPassiveText(theme.passive3.backgroundPix,theme.passive3.useBackground); - pds3->setControlsVisible(false); if(mySettings.general.displayOnStartUp) - { pds3->showFullScreen(); - } } else { @@ -309,40 +307,28 @@ void SoftProjector::positionDisplayWindow() pds3->hide(); } - // check if to display quaternary display screen - if(mySettings.general.displayScreen4>=0) + // Display Screen 4 + int s4 = mySettings.general.displayScreen4; + if(s4 >= 0 && s4 < screenCount) { hasDisplayScreen4 = true; - // if (desktop->isVirtualDesktop()) - { - // Move the display widget to screen 1 (quaternary screen): - pds4->setGeometry(QApplication::primaryScreen()->virtualSiblings().at(mySettings.general.displayScreen4)->geometry()); - pds4->resetImGenSize(); - - } - pds4->setCursor(Qt::BlankCursor); //Sets a Blank Mouse to the screen + pds4->setGeometry(screens.at(s4)->geometry()); + pds4->resetImGenSize(); + pds4->setCursor(Qt::BlankCursor); pds4->renderPassiveText(theme.passive4.backgroundPix,theme.passive4.useBackground); - pds4->setControlsVisible(false); if(mySettings.general.displayOnStartUp) - { pds4->showFullScreen(); - } } else { hasDisplayScreen4 = false; pds4->hide(); } - - // specify that there is more than one diplay screen(monitor) availbale isSingleScreen = false; } else { - // Single monitor only: Do not show on strat up. - // Will be shown only when items were sent to the projector. - qDebug()<< "Setting Primary screen"; - pds1->setGeometry(QApplication::primaryScreen()->virtualSiblings().at(0)->geometry()); + pds1->setGeometry(screens.at(0)->geometry()); pds1->resetImGenSize(); showDisplayScreen(false); isSingleScreen = true; @@ -357,8 +343,6 @@ void SoftProjector::showDisplayScreen(bool show) if(show) { pds1->showFullScreen(); - pds1->positionControls(mySettings.general.displayControls); - pds1->setControlsVisible(true); } else { @@ -412,6 +396,13 @@ void SoftProjector::updateSetting(GeneralSettings &g, Theme &t, SlideShowSetting mySettings.bibleSets4 = bsets4; mySettings.saveSettings(); theme = t; + + // Cache the passive background image in all projectors + pds1->setPassiveBackground(theme.passive.backgroundPix); + if(hasDisplayScreen2) pds2->setPassiveBackground(theme.passive.backgroundPix); + if(hasDisplayScreen3) pds3->setPassiveBackground(theme.passive.backgroundPix); + if(hasDisplayScreen4) pds4->setPassiveBackground(theme.passive.backgroundPix); + bibleWidget->setSettings(mySettings.bibleSets); pictureWidget->setSettings(mySettings.slideSets); @@ -670,7 +661,11 @@ void SoftProjector::setVideo(VideoInfo &video) new_list = true; ui->listShow->clear(); ui->labelSongNotes->setVisible(false); - ui->labelIcon->setPixmap(QPixmap(":/icons/icons/video.png").scaled(16,16,Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); + + // Set appropriate icon based on whether there's a video track + const char* iconPath = video.hasVideo ? ":/icons/icons/video.png" : ":/icons/icons/speaker.png"; + ui->labelIcon->setPixmap(QPixmap(iconPath).scaled(16,16,Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); + ui->labelShow->setText(currentVideo.fileName); new_list = false; updateScreen(); @@ -725,22 +720,30 @@ void SoftProjector::stopVideo() { pds4->stopVideo(); } + mediaPlayer->stopVideo(); } void SoftProjector::setVideoPosition(qint64 position) { - pds1->setVideoPosition(position); - if(hasDisplayScreen2) - { - pds2->setVideoPosition(position); - } - if(hasDisplayScreen3) - { - pds3->setVideoPosition(position); + // Only mute to hide stuttering if we are currently playing. + // If paused, seeking is silent anyway and muting might cause unnecessary backend state changes. + bool shouldMuteGuard = (mediaControls->playbackState() == QMediaPlayer::PlayingState); + bool wasMuted = mediaControls->isMuted(); + + if (shouldMuteGuard) { + pds1->setVideoMuted(true); } - if(hasDisplayScreen4) - { - pds4->setVideoPosition(position); + + pds1->setVideoPosition(position); + if(hasDisplayScreen2) pds2->setVideoPosition(position); + if(hasDisplayScreen3) pds3->setVideoPosition(position); + if(hasDisplayScreen4) pds4->setVideoPosition(position); + + if (shouldMuteGuard) { + // Restore volume/mute state after a short delay + QTimer::singleShot(300, this, [this, wasMuted]() { + pds1->setVideoMuted(wasMuted); + }); } } @@ -1142,6 +1145,24 @@ void SoftProjector::on_actionCloseDisplay_triggered() updateCloseDisplayButtons(ui->actionCloseDisplay->isChecked()); } +void SoftProjector::exitProjector() +{ + qDebug() << "SoftProjector: exitProjector() called from screen UI. Showing:" << showing; + + if (showing) { + // If content is currently showing, first go to passive background + on_actionHide_triggered(); + } else { + // If already on passive background, completely exit the live session + // Ensure media is stopped before closing + stopVideo(); + + // Toggle the display off + ui->actionCloseDisplay->setChecked(false); + on_actionCloseDisplay_triggered(); + } +} + void SoftProjector::updateCloseDisplayButtons(bool isOn) { if(isOn) @@ -1853,6 +1874,7 @@ void SoftProjector::on_actionPrintSchedule_triggered() void SoftProjector::nextSlide() { + qDebug() << "SoftProjector: nextSlide() called."; // selects next item in the show list int current = ui->listShow->currentRow(); if(ui->rbMultiVerse->isChecked()) @@ -1866,12 +1888,25 @@ void SoftProjector::nextSlide() if(current < ui->listShow->count()-1) ui->rbMultiVerse->setChecked(false); } - if(current < ui->listShow->count()-1) + + if(current < ui->listShow->count()-1) { ui->listShow->setCurrentRow(current+1); + } else { + // End of current show list reached. Advance to next item in schedule if possible. + int curScheduleRow = ui->listWidgetSchedule->currentRow(); + if (curScheduleRow < ui->listWidgetSchedule->count() - 1) { + int nextRow = curScheduleRow + 1; + ui->listWidgetSchedule->setCurrentRow(nextRow); + QModelIndex nextIndex = ui->listWidgetSchedule->model()->index(nextRow, 0); + qDebug() << "SoftProjector: Advancing to next schedule item, row:" << nextRow; + on_listWidgetSchedule_doubleClicked(nextIndex); + } + } } void SoftProjector::prevSlide() { + qDebug() << "SoftProjector: prevSlide() called."; // selects previous item in the show list int current = ui->listShow->currentRow(); if(ui->rbMultiVerse->isChecked()) @@ -1888,8 +1923,20 @@ void SoftProjector::prevSlide() if(current>0) ui->rbMultiVerse->setChecked(false); } - if(current>0) + + if(current>0) { ui->listShow->setCurrentRow(current-1); + } else { + // Start of current show list reached. Go to previous item in schedule if possible. + int curScheduleRow = ui->listWidgetSchedule->currentRow(); + if (curScheduleRow > 0) { + int prevRow = curScheduleRow - 1; + ui->listWidgetSchedule->setCurrentRow(prevRow); + QModelIndex prevIndex = ui->listWidgetSchedule->model()->index(prevRow, 0); + qDebug() << "SoftProjector: Going back to previous schedule item, row:" << prevRow; + on_listWidgetSchedule_doubleClicked(prevIndex); + } + } } //void SoftProjector::on_pushButtonPlay_clicked() @@ -2030,6 +2077,7 @@ void SoftProjector::reloadShceduleList() void SoftProjector::on_listWidgetSchedule_doubleClicked(const QModelIndex &index) { + qDebug() << "SoftProjector: on_listWidgetSchedule_doubleClicked row:" << index.row(); Schedule s = schedule.at(index.row()); if(s.stype == "bible") { @@ -2722,6 +2770,14 @@ void SoftProjector::openScheduleItem(QSqlQuery &q, const int scid, VideoInfo &v) v.fileName = q.value(0).toString(); v.filePath = q.value(1).toString(); v.aspectRatio = q.value(2).toInt(); + + // Heuristic: determine if it has video based on common audio extensions + QString ext = v.fileName.split('.').last().toLower(); + if (ext == "mp3" || ext == "m4a" || ext == "wav" || ext == "aac" || ext == "flac" || ext == "ogg" || ext == "wma") { + v.hasVideo = false; + } else { + v.hasVideo = true; // Default to true for other files (videos) + } } void SoftProjector::openScheduleItem(QSqlQuery &q, const int scid, Announcement &a) diff --git a/src/sources/songsettingwidget.cpp b/src/sources/songsettingwidget.cpp index 2130265..5a17be1 100644 --- a/src/sources/songsettingwidget.cpp +++ b/src/sources/songsettingwidget.cpp @@ -173,6 +173,7 @@ void SongSettingWidget::getSettings(SongSettings &settings, SongSettings &settin void SongSettingWidget::loadSettings() { + bool oldBlock = this->blockSignals(true); QPalette p; // Set Effects ui->checkBoxUseShadow->setChecked(mySettings.useShadow); @@ -347,6 +348,8 @@ void SongSettingWidget::loadSettings() ui->groupBoxDisplay4->setChecked(!mySettings4.useDisp1settings); on_groupBoxDisplay4_toggled(!mySettings4.useDisp1settings); + + this->blockSignals(oldBlock); } void SongSettingWidget::setDispScreen2Visible(bool visible) diff --git a/src/sources/theme.cpp b/src/sources/theme.cpp index 377abe5..a5e4b0d 100644 --- a/src/sources/theme.cpp +++ b/src/sources/theme.cpp @@ -435,8 +435,12 @@ void Theme::loadTheme() loadBible(4,bible4); loadSong(1,song); loadSong(2,song2); + loadSong(3,song3); + loadSong(4,song4); loadAnnounce(1,announce); loadAnnounce(2,announce2); + loadAnnounce(3,announce3); + loadAnnounce(4,announce4); } } @@ -451,6 +455,10 @@ void Theme::loadPassive(int screen, TextSettings &settings) settings.backgroundName = sr.field("background_name").value().toString(); settings.backgroundPix.loadFromData(sr.field("background").value().toByteArray()); settings.useDisp1settings = sr.field("use_disp_1").value().toBool(); + + qDebug() << "Theme: Loaded passive for screen" << screen + << "UseBackground:" << settings.useBackground + << "PixSize:" << settings.backgroundPix.size(); } void Theme::loadBible(int screen, BibleSettings &settings) diff --git a/src/sources/videoinfo.cpp b/src/sources/videoinfo.cpp index dd85c04..4d3ceb5 100644 --- a/src/sources/videoinfo.cpp +++ b/src/sources/videoinfo.cpp @@ -22,4 +22,5 @@ VideoInfo::VideoInfo() { aspectRatio = 0; + hasVideo = true; } diff --git a/src/sources/videoplayerwidget.cpp b/src/sources/videoplayerwidget.cpp index f19da87..2089a93 100644 --- a/src/sources/videoplayerwidget.cpp +++ b/src/sources/videoplayerwidget.cpp @@ -32,7 +32,7 @@ VideoPlayerWidget::VideoPlayerWidget(QWidget *parent): QVideoWidget(parent) void VideoPlayerWidget::setFullScreen(bool enabled) { - setFullScreen(enabled); + QVideoWidget::setFullScreen(enabled); } //void VideoPlayerWidget::mouseDoubleClickEvent(QMouseEvent *e)