[UI] Implement achievement notification sound using Qt media player

This commit is contained in:
Herman S.
2025-11-11 15:31:13 +09:00
parent 9bd164bda3
commit 28e89a8a4d
7 changed files with 62 additions and 32 deletions
+2 -2
View File
@@ -85,7 +85,7 @@ jobs:
pip install aqtinstall
echo "Available architectures for Qt 6.9.2:"
aqt list-qt linux desktop --arch 6.9.2 || true
aqt install-qt linux desktop 6.9.2 linux_gcc_64 -O /opt/Qt
aqt install-qt linux desktop 6.9.2 linux_gcc_64 -m qtmultimedia -O /opt/Qt
fi
export PATH="/opt/Qt/6.9.2/gcc_64/bin:$PATH"
export CMAKE_PREFIX_PATH="/opt/Qt/6.9.2/gcc_64"
@@ -257,7 +257,7 @@ jobs:
$qtCacheHit = '${{ steps.cache-qt.outputs.cache-hit }}'
if ($qtCacheHit -ne 'true') {
pip install aqtinstall
aqt install-qt windows desktop 6.9.2 win64_msvc2022_64 -O C:\Qt
aqt install-qt windows desktop 6.9.2 win64_msvc2022_64 -m qtmultimedia -O C:\Qt
}
$cacheHit = '${{ steps.cache-submodules.outputs.cache-hit }}'
+2 -2
View File
@@ -20,7 +20,7 @@ drivers.
* Install using aqtinstall:
```
pip install aqtinstall
python -m aqt install-qt windows desktop 6.9.2 win64_msvc2022_64 -O C:\Qt
python -m aqt install-qt windows desktop 6.9.2 win64_msvc2022_64 -m qtmultimedia -O C:\Qt
# Note: msvc2022_64 is compatible with with VS2026
```
* The build script will automatically detect it if installed in `C:\Qt`
@@ -127,7 +127,7 @@ You can install Qt from your distribution's package manager, or use aqtinstall:
```sh
pip install aqtinstall
aqt install-qt linux desktop 6.9.2 linux_gcc_64 -O /opt/Qt
aqt install-qt linux desktop 6.9.2 linux_gcc_64 -m qtmultimedia -O /opt/Qt
```
If using aqtinstall, add Qt to your environment:
+5
View File
@@ -176,6 +176,7 @@ filter("platforms:Linux")
path.join(qt_dir, "include/QtCore"),
path.join(qt_dir, "include/QtGui"),
path.join(qt_dir, "include/QtWidgets"),
path.join(qt_dir, "include/QtMultimedia"),
})
libdirs({
path.join(qt_dir, "lib"),
@@ -191,6 +192,7 @@ filter("platforms:Linux")
"Qt6Core",
"Qt6Gui",
"Qt6Widgets",
"Qt6Multimedia",
})
end
@@ -332,6 +334,7 @@ filter("platforms:Windows")
path.join(qt_dir, "include/QtCore"),
path.join(qt_dir, "include/QtGui"),
path.join(qt_dir, "include/QtWidgets"),
path.join(qt_dir, "include/QtMultimedia"),
})
libdirs({
path.join(qt_dir, "lib"),
@@ -344,6 +347,7 @@ filter({"platforms:Windows", "configurations:Release"})
"Qt6Core",
"Qt6Gui",
"Qt6Widgets",
"Qt6Multimedia",
})
end
@@ -353,6 +357,7 @@ filter({"platforms:Windows", "configurations:Debug or Checked"})
"Qt6Cored",
"Qt6Guid",
"Qt6Widgetsd",
"Qt6Multimediad",
})
end
+17 -5
View File
@@ -312,8 +312,9 @@ QWidget* ConfigDialogQt::CreateEditorWidget(ConfigVarInfo* var_info) {
connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigDialogQt::OnValueChanged);
return combo;
} else if (dynamic_cast<cvar::ConfigVar<std::filesystem::path>*>(
var_info->var)) {
} else if (auto* path_var =
dynamic_cast<cvar::ConfigVar<std::filesystem::path>*>(
var_info->var)) {
// Path input with browse button
auto* container = new QWidget();
auto* layout = new QHBoxLayout(container);
@@ -328,9 +329,20 @@ QWidget* ConfigDialogQt::CreateEditorWidget(ConfigVarInfo* var_info) {
auto* browse_button = new QPushButton("Browse...");
connect(browse_button, &QPushButton::clicked,
[this, line_edit, var_info]() {
QString path = QFileDialog::getExistingDirectory(
this, SafeQString("Select Directory for " + var_info->name),
line_edit->text());
QString path;
// Special case for notification_sound_path - file picker with
// audio filters
if (var_info->name == "notification_sound_path") {
path = QFileDialog::getOpenFileName(
this, "Select Notification Sound", line_edit->text(),
"Audio Files (*.wav *.mp3 *.ogg *.flac *.m4a *.aac *.wma "
"*.opus);;All Files (*)");
} else {
// Default: directory picker
path = QFileDialog::getExistingDirectory(
this, SafeQString("Select Directory for " + var_info->name),
line_edit->text());
}
if (!path.isEmpty()) {
line_edit->setText(path);
}
-22
View File
@@ -14,15 +14,6 @@
#include "xenia/ui/imgui_guest_notification.h"
#include "xenia/ui/imgui_notification.h"
#if XE_PLATFORM_WIN32
#include <playsoundapi.h>
#endif
DEFINE_string(notification_sound_path, "",
"Path (including filename) to selected notification sound. Sound "
"MUST be in wav format!",
"General");
namespace xe {
namespace ui {
@@ -46,19 +37,6 @@ void ImGuiGuestNotification::UpdateNotificationState() {
// TODO(Gliniak): Implement delayed notifications.
current_stage_ = NotificationStage::FazeIn;
notification_draw_progress_ = 0.2f;
#if XE_PLATFORM_WIN32
if (!cvars::notification_sound_path.empty()) {
auto notification_sound_path = cvars::notification_sound_path;
if (std::filesystem::exists(notification_sound_path)) {
PlaySound(std::wstring(notification_sound_path.begin(),
notification_sound_path.end())
.c_str(),
NULL,
SND_FILENAME | SND_NODEFAULT | SND_NOSTOP | SND_ASYNC);
}
}
#endif
break;
case NotificationStage::FazeIn: {
SetCreationTime(Clock::QueryHostUptimeMillis());
+32 -1
View File
@@ -9,16 +9,29 @@
#include "xenia/ui/notification_widget_qt.h"
#include <QUrl>
#include <QVBoxLayout>
#include <filesystem>
#include "xenia/base/cvar.h"
#include "xenia/base/string.h"
#include "xenia/ui/qt_util.h"
DEFINE_path(notification_sound_path, "",
"Path (including filename) to selected notification sound. "
"Supports WAV, MP3, OGG, FLAC, and other common formats.",
"UI");
namespace xe {
namespace app {
using xe::ui::SafeQString;
NotificationWidgetQt::NotificationWidgetQt(QWidget* parent,
const QString& title,
const QString& message,
int duration_ms)
: QWidget(parent) {
: QWidget(parent), media_player_(nullptr), audio_output_(nullptr) {
setAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_TransparentForMouseEvents);
setAutoFillBackground(true);
@@ -57,6 +70,19 @@ NotificationWidgetQt::NotificationWidgetQt(QWidget* parent,
deleteLater(); // Ensure widget is actually destroyed
});
auto_close_timer_->setInterval(duration_ms);
// Setup media player for notification sound
if (!cvars::notification_sound_path.empty()) {
std::filesystem::path sound_path = cvars::notification_sound_path;
if (std::filesystem::exists(sound_path)) {
media_player_ = new QMediaPlayer(this);
audio_output_ = new QAudioOutput(this);
media_player_->setAudioOutput(audio_output_);
media_player_->setSource(
QUrl::fromLocalFile(SafeQString(xe::path_to_utf8(sound_path))));
audio_output_->setVolume(1.0);
}
}
}
void NotificationWidgetQt::Show() {
@@ -74,6 +100,11 @@ void NotificationWidgetQt::Show() {
show();
raise();
auto_close_timer_->start();
// Play sound if configured
if (media_player_) {
media_player_->play();
}
}
} // namespace app
+4
View File
@@ -10,7 +10,9 @@
#ifndef XENIA_UI_NOTIFICATION_WIDGET_QT_H_
#define XENIA_UI_NOTIFICATION_WIDGET_QT_H_
#include <QAudioOutput>
#include <QLabel>
#include <QMediaPlayer>
#include <QTimer>
#include <QWidget>
@@ -29,6 +31,8 @@ class NotificationWidgetQt : public QWidget {
private:
QTimer* auto_close_timer_;
QMediaPlayer* media_player_;
QAudioOutput* audio_output_;
};
} // namespace app