diff --git a/UI/cmake/legacy.cmake b/UI/cmake/legacy.cmake index 709e9c7ab485bb..524e267c702879 100644 --- a/UI/cmake/legacy.cmake +++ b/UI/cmake/legacy.cmake @@ -211,6 +211,8 @@ target_sources( spinbox-ignorewheel.hpp source-tree.cpp source-tree.hpp + system-tray-icon.cpp + system-tray-icon.hpp url-push-button.cpp url-push-button.hpp undo-stack-obs.cpp diff --git a/UI/cmake/ui-elements.cmake b/UI/cmake/ui-elements.cmake index d3f6f44ec29aac..f2118a6ccee831 100644 --- a/UI/cmake/ui-elements.cmake +++ b/UI/cmake/ui-elements.cmake @@ -20,6 +20,8 @@ target_sources( slider-ignorewheel.hpp spinbox-ignorewheel.cpp spinbox-ignorewheel.hpp + system-tray-icon.cpp + system-tray-icon.hpp vertical-scroll-area.cpp vertical-scroll-area.hpp) diff --git a/UI/system-tray-icon.cpp b/UI/system-tray-icon.cpp new file mode 100644 index 00000000000000..25f3c120c5fcc4 --- /dev/null +++ b/UI/system-tray-icon.cpp @@ -0,0 +1,289 @@ +#include "system-tray-icon.hpp" +#include "window-basic-main.hpp" + +#include + +OBSSystemTrayIcon::OBSSystemTrayIcon(OBSBasic *main) : QSystemTrayIcon(nullptr) +{ +#ifdef __APPLE__ + QIcon trayIconFile = QIcon(":/res/images/obs_macos.svg"); + trayIconFile.setIsMask(true); +#else + QIcon trayIconFile = QIcon(":/res/images/obs.png"); +#endif + setIcon(QIcon::fromTheme("obs-tray", trayIconFile)); + setToolTip("OBS Studio"); + + menu.reset(new QMenu()); + + showHide = menu->addAction( + main->isVisible() ? QTStr("Basic.SystemTray.Hide") + : QTStr("Basic.SystemTray.Show"), + this, [this]() { emit VisibilityActionTriggered(); }); + menu->addSeparator(); + + previewProjector.reset(new QMenu(QTStr("PreviewProjector"))); + studioProgramProjector.reset( + new QMenu(QTStr("StudioProgramProjector"))); + + main->AddProjectorMenuMonitors(previewProjector.data(), main, + &OBSBasic::OpenPreviewProjector); + main->AddProjectorMenuMonitors(studioProgramProjector.data(), main, + &OBSBasic::OpenStudioProgramProjector); + menu->addMenu(previewProjector.data()); + menu->addMenu(studioProgramProjector.data()); + menu->addSeparator(); + + stream = menu->addAction(main->StreamingActive() + ? QTStr("Basic.Main.StopStreaming") + : QTStr("Basic.Main.StartStreaming"), + main, + [this]() { emit StreamActionTriggered(); }); + record = menu->addAction(main->RecordingActive() + ? QTStr("Basic.Main.StopRecording") + : QTStr("Basic.Main.StartRecording"), + main, + [this]() { emit RecordActionTriggered(); }); + replay = menu->addAction( + main->ReplayBufferActive() + ? QTStr("Basic.Main.StopReplayBuffer") + : QTStr("Basic.Main.StartReplayBuffer"), + main, [this]() { emit ReplayBufferActionTriggered(); }); + virtualCam = menu->addAction( + main->VirtualCamActive() ? QTStr("Basic.Main.StopVirtualCam") + : QTStr("Basic.Main.StartVirtualCam"), + main, [this]() { emit VirtualCamActionTriggered(); }); + menu->addSeparator(); + menu->addAction(QTStr("Exit"), main, &OBSBasic::close); + + setContextMenu(menu.data()); + + if (main->Active()) + OnActivate(true); + + connect(this, &QSystemTrayIcon::activated, this, + &OBSSystemTrayIcon::IconActivated); + + /* Set up state update connections */ + connect(main, &OBSBasic::VisibilityChanged, this, + &OBSSystemTrayIcon::VisibilityChanged); + + connect(main, &OBSBasic::StreamingPreparing, this, + &OBSSystemTrayIcon::StreamingPreparing); + connect(main, &OBSBasic::StreamingStarting, this, + &OBSSystemTrayIcon::StreamingStarting); + connect(main, &OBSBasic::StreamingStarted, this, + &OBSSystemTrayIcon::StreamingStarted); + connect(main, &OBSBasic::StreamingStopping, this, + &OBSSystemTrayIcon::StreamingStopping); + connect(main, &OBSBasic::StreamingStopped, this, + &OBSSystemTrayIcon::StreamingStopped); + + connect(main, &OBSBasic::RecordingStarted, this, + &OBSSystemTrayIcon::RecordingStarted); + connect(main, &OBSBasic::RecordingPaused, this, + &OBSSystemTrayIcon::RecordingPaused); + connect(main, &OBSBasic::RecordingUnpaused, this, + &OBSSystemTrayIcon::RecordingUnpaused); + connect(main, &OBSBasic::RecordingStopping, this, + &OBSSystemTrayIcon::RecordingStopping); + connect(main, &OBSBasic::RecordingStopped, this, + &OBSSystemTrayIcon::RecordingStopped); + + connect(main, &OBSBasic::ReplayBufStarted, this, + &OBSSystemTrayIcon::ReplayBufferStarted); + connect(main, &OBSBasic::ReplayBufferStopping, this, + &OBSSystemTrayIcon::ReplayBufferStopping); + connect(main, &OBSBasic::ReplayBufStopped, this, + &OBSSystemTrayIcon::ReplayBufferStopped); + + connect(main, &OBSBasic::VirtualCamStarted, this, + &OBSSystemTrayIcon::VirtualCamStarted); + connect(main, &OBSBasic::VirtualCamStopped, this, + &OBSSystemTrayIcon::VirtualCamStopped); + + /* Set up enablement connection */ + connect(main, &OBSBasic::ReplayBufEnabled, this, + &OBSSystemTrayIcon::UpdateReplayBuffer); + connect(main, &OBSBasic::VirtualCamEnabled, this, + &OBSSystemTrayIcon::EnableVirtualCam); + + show(); +} + +OBSSystemTrayIcon::~OBSSystemTrayIcon() {} + +void OBSSystemTrayIcon::OnActivate(bool force) +{ + if (!force && OBSBasic::Get()->Active()) + return; + +#ifdef __APPLE__ + QIcon trayIconFile = QIcon(":/res/images/tray_active_macos.svg"); + trayIconFile.setIsMask(true); +#else + QIcon trayIconFile = QIcon(":/res/images/tray_active.png"); +#endif + setIcon(QIcon::fromTheme("obs-tray-active", trayIconFile)); +} + +void OBSSystemTrayIcon::OnDeactivate() +{ + if (!OBSBasic::Get()->Active()) + return; + +#ifdef __APPLE__ + QIcon trayIconFile = QIcon(":/res/images/obs_macos.svg"); + trayIconFile.setIsMask(true); +#else + QIcon trayIconFile = QIcon(":/res/images/obs.png"); +#endif + setIcon(QIcon::fromTheme("obs-tray", trayIconFile)); +} + +// Visibility +void OBSSystemTrayIcon::VisibilityChanged(bool visible) +{ + showHide->setText(visible ? QTStr("Basic.SystemTray.Hide") + : QTStr("Basic.SystemTray.Show")); +} + +// Streaming +void OBSSystemTrayIcon::StreamingPreparing() +{ + stream->setEnabled(false); + stream->setText(QTStr("Basic.Main.PreparingStream")); +} + +void OBSSystemTrayIcon::StreamingStarting(bool) +{ + stream->setText(QTStr("Basic.Main.Connecting")); +} + +void OBSSystemTrayIcon::StreamingStarted(bool) +{ + stream->setEnabled(true); + stream->setText(QTStr("Basic.Main.StopStreaming")); + OnActivate(); +} + +void OBSSystemTrayIcon::StreamingStopping() +{ + stream->setText(QTStr("Basic.Main.StoppingStreaming")); +} + +void OBSSystemTrayIcon::StreamingStopped(bool) +{ + stream->setText(QTStr("Basic.Main.StartStreaming")); + OnDeactivate(); +} + +// Recording +void OBSSystemTrayIcon::RecordingStarted(bool) +{ + record->setText(QTStr("Basic.Main.StopRecording")); + OnActivate(); +} + +void OBSSystemTrayIcon::RecordingStopping() +{ + record->setText(QTStr("Basic.Main.StoppingRecording")); +} + +void OBSSystemTrayIcon::RecordingStopped() +{ + record->setText(QTStr("Basic.Main.StartRecording")); + OnDeactivate(); +} + +void OBSSystemTrayIcon::RecordingPaused() +{ +#ifdef __APPLE__ + QIcon trayIconFile = QIcon(":/res/images/obs_paused_macos.svg"); + trayIconFile.setIsMask(true); +#else + QIcon trayIconFile = QIcon(":/res/images/obs_paused.png"); +#endif + setIcon(QIcon::fromTheme("obs-tray-paused", trayIconFile)); +} + +void OBSSystemTrayIcon::RecordingUnpaused() +{ +#ifdef __APPLE__ + QIcon trayIconFile = QIcon(":/res/images/tray_active_macos.svg"); + trayIconFile.setIsMask(true); +#else + QIcon trayIconFile = QIcon(":/res/images/tray_active.png"); +#endif + setIcon(QIcon::fromTheme("obs-tray-active", trayIconFile)); +} + +// Replay Buffer +void OBSSystemTrayIcon::UpdateReplayBuffer(bool enable) +{ + replay->setEnabled(enable); +} + +void OBSSystemTrayIcon::ReplayBufferStarted() +{ + replay->setText(QTStr("Basic.Main.StopReplayBuffer")); + OnActivate(); +} + +void OBSSystemTrayIcon::ReplayBufferStopping() +{ + replay->setText(QTStr("Basic.Main.StoppingReplayBuffer")); +} + +void OBSSystemTrayIcon::ReplayBufferStopped() +{ + replay->setText(QTStr("Basic.Main.StartReplayBuffer")); + OnDeactivate(); +} + +// Virtual Camera +void OBSSystemTrayIcon::EnableVirtualCam() +{ + virtualCam->setEnabled(true); +} + +void OBSSystemTrayIcon::VirtualCamStarted() +{ + virtualCam->setText(QTStr("Basic.Main.StopVirtualCam")); + OnActivate(); +} + +void OBSSystemTrayIcon::VirtualCamStopped() +{ + virtualCam->setText(QTStr("Basic.Main.StartVirtualCam")); + OnDeactivate(); +} + +void OBSSystemTrayIcon::IconActivated(QSystemTrayIcon::ActivationReason reason) +{ + OBSBasic *main = OBSBasic::Get(); + + // Refresh projector list + previewProjector.data()->clear(); + studioProgramProjector.data()->clear(); + main->AddProjectorMenuMonitors(previewProjector.data(), main, + &OBSBasic::OpenPreviewProjector); + main->AddProjectorMenuMonitors(studioProgramProjector.data(), main, + &OBSBasic::OpenStudioProgramProjector); + +#ifdef __APPLE__ + UNUSED_PARAMETER(reason); +#else + if (reason == QSystemTrayIcon::Trigger) + emit VisibilityActionTriggered(); +#endif +} + +void OBSSystemTrayIcon::ShowNotification(const QString &text, + QSystemTrayIcon::MessageIcon n) +{ + if (!supportsMessages()) + return; + + showMessage("OBS Studio", text, n, 10000); +} diff --git a/UI/system-tray-icon.hpp b/UI/system-tray-icon.hpp new file mode 100644 index 00000000000000..4002b628d2d290 --- /dev/null +++ b/UI/system-tray-icon.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +class QAction; +class OBSBasic; + +class OBSSystemTrayIcon : public QSystemTrayIcon { + Q_OBJECT + +private: + QScopedPointer menu; + QPointer showHide; + QPointer stream; + QPointer record; + QPointer replay; + QPointer virtualCam; + QScopedPointer previewProjector; + QScopedPointer studioProgramProjector; + + void OnActivate(bool force = false); + void OnDeactivate(); + +private slots: + void VisibilityChanged(bool visible); + void IconActivated(QSystemTrayIcon::ActivationReason reason); + + void StreamingPreparing(); + void StreamingStarting(bool broadcastAutoStart); + void StreamingStarted(bool delay); + void StreamingStopping(); + void StreamingStopped(bool delay); + + void RecordingStarted(bool pausible); + void RecordingStopping(); + void RecordingStopped(); + void RecordingPaused(); + void RecordingUnpaused(); + + void ReplayBufferStarted(); + void ReplayBufferStopping(); + void ReplayBufferStopped(); + + void VirtualCamStarted(); + void VirtualCamStopped(); + + void UpdateReplayBuffer(bool enable); + void EnableVirtualCam(); + +public: + OBSSystemTrayIcon(OBSBasic *main); + ~OBSSystemTrayIcon(); + + void ShowNotification(const QString &text, + QSystemTrayIcon::MessageIcon n); + +signals: + void VisibilityActionTriggered(); + void StreamActionTriggered(); + void RecordActionTriggered(); + void ReplayBufferActionTriggered(); + void VirtualCamActionTriggered(); +}; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index b8e9d4f281d9a1..e962a4ce146fd1 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -76,6 +76,7 @@ #include "ui-validation.hpp" #include "media-controls.hpp" #include "undo-stack-obs.hpp" +#include "system-tray-icon.hpp" #include #include @@ -2045,10 +2046,6 @@ void OBSBasic::ResetOutputs() emit ReplayBufEnabled(outputHandler->replayBuffer); - if (sysTrayReplayBuffer) - sysTrayReplayBuffer->setEnabled( - !!outputHandler->replayBuffer); - UpdateIsRecordingPausable(); } else { outputHandler->Update(); @@ -3066,7 +3063,6 @@ OBSBasic::~OBSBasic() delete deinterlaceMenu; delete perSceneTransitionMenu; delete shortcutFilter; - delete trayMenu; delete programOptions; delete program; @@ -5337,8 +5333,7 @@ void OBSBasic::changeEvent(QEvent *event) (QWindowStateChangeEvent *)event; if (isMinimized()) { - if (trayIcon && trayIcon->isVisible() && - sysTrayMinimizeToTray()) { + if (trayIcon && sysTrayMinimizeToTray()) { ToggleShowHide(); return; } @@ -6954,11 +6949,6 @@ void OBSBasic::DisplayStreamStartError() emit StreamingStopped(); - if (sysTrayStream) { - sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); - sysTrayStream->setEnabled(true); - } - QMessageBox::critical(this, QTStr("Output.StartStreamFailed"), message); } @@ -7104,11 +7094,6 @@ void OBSBasic::StartStreaming() emit StreamingPreparing(); - if (sysTrayStream) { - sysTrayStream->setEnabled(false); - sysTrayStream->setText("Basic.Main.PreparingStream"); - } - auto holder = outputHandler->SetupStreaming(service); auto future = holder.future.then(this, [&](bool setupStreamingResult) { if (!setupStreamingResult) { @@ -7123,9 +7108,6 @@ void OBSBasic::StartStreaming() emit StreamingStarting(autoStartBroadcast); - if (sysTrayStream) - sysTrayStream->setText("Basic.Main.Connecting"); - if (!outputHandler->StartStreaming(service)) { DisplayStreamStartError(); return; @@ -7294,19 +7276,6 @@ inline void OBSBasic::OnActivate(bool force) lastOutputResolution = {ovi.base_width, ovi.base_height}; TaskbarOverlaySetStatus(TaskbarOverlayStatusActive); - if (trayIcon && trayIcon->isVisible()) { -#ifdef __APPLE__ - QIcon trayMask = - QIcon(":/res/images/tray_active_macos.svg"); - trayMask.setIsMask(true); - trayIcon->setIcon( - QIcon::fromTheme("obs-tray", trayMask)); -#else - trayIcon->setIcon(QIcon::fromTheme( - "obs-tray-active", - QIcon(":/res/images/tray_active.png"))); -#endif - } } } @@ -7322,44 +7291,11 @@ inline void OBSBasic::OnDeactivate() ClearProcessPriority(); TaskbarOverlaySetStatus(TaskbarOverlayStatusInactive); - if (trayIcon && trayIcon->isVisible()) { -#ifdef __APPLE__ - QIcon trayIconFile = - QIcon(":/res/images/obs_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = QIcon(":/res/images/obs.png"); -#endif - trayIcon->setIcon( - QIcon::fromTheme("obs-tray", trayIconFile)); - } - } else if (outputHandler->Active() && trayIcon && - trayIcon->isVisible()) { - if (os_atomic_load_bool(&recording_paused)) { -#ifdef __APPLE__ - QIcon trayIconFile = - QIcon(":/res/images/obs_paused_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = - QIcon(":/res/images/obs_paused.png"); -#endif - trayIcon->setIcon(QIcon::fromTheme("obs-tray-paused", - trayIconFile)); + } else if (outputHandler->Active()) { + if (os_atomic_load_bool(&recording_paused)) TaskbarOverlaySetStatus(TaskbarOverlayStatusPaused); - } else { -#ifdef __APPLE__ - QIcon trayIconFile = - QIcon(":/res/images/tray_active_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = - QIcon(":/res/images/tray_active.png"); -#endif - trayIcon->setIcon(QIcon::fromTheme("obs-tray-active", - trayIconFile)); + else TaskbarOverlaySetStatus(TaskbarOverlayStatusActive); - } } } @@ -7450,26 +7386,13 @@ void OBSBasic::ForceStopStreaming() void OBSBasic::StreamDelayStarting(int sec) { emit StreamingStarted(true); - - if (sysTrayStream) { - sysTrayStream->setText(QTStr("Basic.Main.StopStreaming")); - sysTrayStream->setEnabled(true); - } - ui->statusbar->StreamDelayStarting(sec); - OnActivate(); } void OBSBasic::StreamDelayStopping(int sec) { emit StreamingStopped(true); - - if (sysTrayStream) { - sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); - sysTrayStream->setEnabled(true); - } - ui->statusbar->StreamDelayStopping(sec); if (api) @@ -7482,11 +7405,6 @@ void OBSBasic::StreamingStart() OBSOutputAutoRelease output = obs_frontend_get_streaming_output(); ui->statusbar->StreamStarted(output); - if (sysTrayStream) { - sysTrayStream->setText(QTStr("Basic.Main.StopStreaming")); - sysTrayStream->setEnabled(true); - } - #ifdef YOUTUBE_ENABLED if (!autoStartBroadcast) { // get a current stream key @@ -7520,10 +7438,6 @@ void OBSBasic::StreamingStart() void OBSBasic::StreamStopping() { emit StreamingStopping(); - - if (sysTrayStream) - sysTrayStream->setText(QTStr("Basic.Main.StoppingStreaming")); - streamingStopping = true; if (api) api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); @@ -7581,11 +7495,6 @@ void OBSBasic::StreamingStop(int code, QString last_error) emit StreamingStopped(); - if (sysTrayStream) { - sysTrayStream->setText(QTStr("Basic.Main.StartStreaming")); - sysTrayStream->setEnabled(true); - } - streamingStopping = false; if (api) api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPED); @@ -7732,9 +7641,6 @@ void OBSBasic::RecordStopping() { emit RecordingStopping(); - if (sysTrayRecord) - sysTrayRecord->setText(QTStr("Basic.Main.StoppingRecording")); - recordingStopping = true; if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); @@ -7755,9 +7661,6 @@ void OBSBasic::RecordingStart() ui->statusbar->RecordingStarted(outputHandler->fileOutput); emit RecordingStarted(isRecordingPausable); - if (sysTrayRecord) - sysTrayRecord->setText(QTStr("Basic.Main.StopRecording")); - recordingStopping = false; if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); @@ -7775,9 +7678,6 @@ void OBSBasic::RecordingStop(int code, QString last_error) ui->statusbar->RecordingStopped(); emit RecordingStopped(); - if (sysTrayRecord) - sysTrayRecord->setText(QTStr("Basic.Main.StartRecording")); - blog(LOG_INFO, RECORDING_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { @@ -7924,10 +7824,6 @@ void OBSBasic::ReplayBufferStopping() emit ReplayBufStopping(); - if (sysTrayReplayBuffer) - sysTrayReplayBuffer->setText( - QTStr("Basic.Main.StoppingReplayBuffer")); - replayBufferStopping = true; if (api) api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING); @@ -7953,10 +7849,6 @@ void OBSBasic::ReplayBufferStart() emit ReplayBufStarted(); - if (sysTrayReplayBuffer) - sysTrayReplayBuffer->setText( - QTStr("Basic.Main.StopReplayBuffer")); - replayBufferStopping = false; if (api) api->on_event(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED); @@ -8011,10 +7903,6 @@ void OBSBasic::ReplayBufferStop(int code) emit ReplayBufStopped(); - if (sysTrayReplayBuffer) - sysTrayReplayBuffer->setText( - QTStr("Basic.Main.StartReplayBuffer")); - blog(LOG_INFO, REPLAY_BUFFER_STOP); if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { @@ -8083,9 +7971,6 @@ void OBSBasic::OnVirtualCamStart() emit VirtualCamStarted(); - if (sysTrayVirtualCam) - sysTrayVirtualCam->setText(QTStr("Basic.Main.StopVirtualCam")); - if (api) api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED); @@ -8101,9 +7986,6 @@ void OBSBasic::OnVirtualCamStop(int) emit VirtualCamStopped(); - if (sysTrayVirtualCam) - sysTrayVirtualCam->setText(QTStr("Basic.Main.StartVirtualCam")); - if (api) api->on_event(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED); @@ -9818,8 +9700,6 @@ void OBSBasic::SetShowing(bool showing) } } - if (showHide) - showHide->setText(QTStr("Basic.SystemTray.Show")); QTimer::singleShot(0, this, &OBSBasic::hide); if (previewEnabled) @@ -9830,8 +9710,6 @@ void OBSBasic::SetShowing(bool showing) #endif } else if (showing && !isVisible()) { - if (showHide) - showHide->setText(QTStr("Basic.SystemTray.Hide")); QTimer::singleShot(0, this, &OBSBasic::show); if (previewEnabled) @@ -9863,6 +9741,8 @@ void OBSBasic::SetShowing(bool showing) setWindowState(state); } } + + emit VisibilityChanged(showing); } void OBSBasic::ToggleShowHide() @@ -9877,130 +9757,42 @@ void OBSBasic::ToggleShowHide() SetShowing(!showing); } -void OBSBasic::SystemTrayInit() -{ -#ifdef __APPLE__ - QIcon trayIconFile = QIcon(":/res/images/obs_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = QIcon(":/res/images/obs.png"); -#endif - trayIcon.reset(new QSystemTrayIcon( - QIcon::fromTheme("obs-tray", trayIconFile), this)); - trayIcon->setToolTip("OBS Studio"); - - showHide = new QAction(QTStr("Basic.SystemTray.Show"), trayIcon.data()); - sysTrayStream = new QAction( - StreamingActive() ? QTStr("Basic.Main.StopStreaming") - : QTStr("Basic.Main.StartStreaming"), - trayIcon.data()); - sysTrayRecord = new QAction( - RecordingActive() ? QTStr("Basic.Main.StopRecording") - : QTStr("Basic.Main.StartRecording"), - trayIcon.data()); - sysTrayReplayBuffer = new QAction( - ReplayBufferActive() ? QTStr("Basic.Main.StopReplayBuffer") - : QTStr("Basic.Main.StartReplayBuffer"), - trayIcon.data()); - sysTrayVirtualCam = new QAction( - VirtualCamActive() ? QTStr("Basic.Main.StopVirtualCam") - : QTStr("Basic.Main.StartVirtualCam"), - trayIcon.data()); - exit = new QAction(QTStr("Exit"), trayIcon.data()); - - trayMenu = new QMenu; - previewProjector = new QMenu(QTStr("PreviewProjector")); - studioProgramProjector = new QMenu(QTStr("StudioProgramProjector")); - AddProjectorMenuMonitors(previewProjector, this, - &OBSBasic::OpenPreviewProjector); - AddProjectorMenuMonitors(studioProgramProjector, this, - &OBSBasic::OpenStudioProgramProjector); - trayMenu->addAction(showHide); - trayMenu->addSeparator(); - trayMenu->addMenu(previewProjector); - trayMenu->addMenu(studioProgramProjector); - trayMenu->addSeparator(); - trayMenu->addAction(sysTrayStream); - trayMenu->addAction(sysTrayRecord); - trayMenu->addAction(sysTrayReplayBuffer); - trayMenu->addAction(sysTrayVirtualCam); - trayMenu->addSeparator(); - trayMenu->addAction(exit); - trayIcon->setContextMenu(trayMenu); - trayIcon->show(); - - if (outputHandler && !outputHandler->replayBuffer) - sysTrayReplayBuffer->setEnabled(false); - - sysTrayVirtualCam->setEnabled(vcamEnabled); - - if (Active()) - OnActivate(true); - - connect(trayIcon.data(), &QSystemTrayIcon::activated, this, - &OBSBasic::IconActivated); - connect(showHide, &QAction::triggered, this, &OBSBasic::ToggleShowHide); - connect(sysTrayStream, &QAction::triggered, this, - &OBSBasic::StreamActionTriggered); - connect(sysTrayRecord, &QAction::triggered, this, - &OBSBasic::RecordActionTriggered); - connect(sysTrayReplayBuffer.data(), &QAction::triggered, this, - &OBSBasic::ReplayBufferActionTriggered); - connect(sysTrayVirtualCam.data(), &QAction::triggered, this, - &OBSBasic::VirtualCamActionTriggered); - connect(exit, &QAction::triggered, this, &OBSBasic::close); -} - -void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) -{ - // Refresh projector list - previewProjector->clear(); - studioProgramProjector->clear(); - AddProjectorMenuMonitors(previewProjector, this, - &OBSBasic::OpenPreviewProjector); - AddProjectorMenuMonitors(studioProgramProjector, this, - &OBSBasic::OpenStudioProgramProjector); - -#ifdef __APPLE__ - UNUSED_PARAMETER(reason); -#else - if (reason == QSystemTrayIcon::Trigger) { - EnablePreviewDisplay(previewEnabled && !isVisible()); - ToggleShowHide(); - } -#endif -} - void OBSBasic::SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n) { - if (trayIcon && trayIcon->isVisible() && - QSystemTrayIcon::supportsMessages()) { - QSystemTrayIcon::MessageIcon icon = - QSystemTrayIcon::MessageIcon(n); - trayIcon->showMessage("OBS Studio", text, icon, 10000); - } + if (trayIcon) + trayIcon->ShowNotification(text, n); } void OBSBasic::SystemTray(bool firstStarted) { if (!QSystemTrayIcon::isSystemTrayAvailable()) return; - if (!trayIcon && !firstStarted) - return; bool sysTrayWhenStarted = config_get_bool( GetGlobalConfig(), "BasicWindow", "SysTrayWhenStarted"); bool sysTrayEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", "SysTrayEnabled"); - if (firstStarted) - SystemTrayInit(); + if (sysTrayEnabled) { + trayIcon.reset(new OBSSystemTrayIcon(this)); + + connect(trayIcon.data(), + &OBSSystemTrayIcon::VisibilityActionTriggered, this, + &OBSBasic::ToggleShowHide); + connect(trayIcon.data(), + &OBSSystemTrayIcon::StreamActionTriggered, this, + &OBSBasic::StreamActionTriggered); + connect(trayIcon.data(), + &OBSSystemTrayIcon::RecordActionTriggered, this, + &OBSBasic::RecordActionTriggered); + connect(trayIcon.data(), + &OBSSystemTrayIcon::ReplayBufferActionTriggered, this, + &OBSBasic::ReplayBufferActionTriggered); + connect(trayIcon.data(), + &OBSSystemTrayIcon::VirtualCamActionTriggered, this, + &OBSBasic::VirtualCamActionTriggered); - if (!sysTrayEnabled) { - trayIcon->hide(); - } else { - trayIcon->show(); if (firstStarted && (sysTrayWhenStarted || opt_minimize_tray)) { EnablePreviewDisplay(false); #ifdef __APPLE__ @@ -10008,12 +9800,9 @@ void OBSBasic::SystemTray(bool firstStarted) #endif opt_minimize_tray = false; } + } else { + trayIcon.reset(); } - - if (isVisible()) - showHide->setText(QTStr("Basic.SystemTray.Hide")); - else - showHide->setText(QTStr("Basic.SystemTray.Show")); } bool OBSBasic::sysTrayMinimizeToTray() @@ -10725,18 +10514,6 @@ void OBSBasic::PauseRecording() ui->statusbar->RecordingPaused(); TaskbarOverlaySetStatus(TaskbarOverlayStatusPaused); - if (trayIcon && trayIcon->isVisible()) { -#ifdef __APPLE__ - QIcon trayIconFile = - QIcon(":/res/images/obs_paused_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = - QIcon(":/res/images/obs_paused.png"); -#endif - trayIcon->setIcon(QIcon::fromTheme("obs-tray-paused", - trayIconFile)); - } if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_PAUSED); @@ -10763,18 +10540,6 @@ void OBSBasic::UnpauseRecording() ui->statusbar->RecordingUnpaused(); TaskbarOverlaySetStatus(TaskbarOverlayStatusActive); - if (trayIcon && trayIcon->isVisible()) { -#ifdef __APPLE__ - QIcon trayIconFile = - QIcon(":/res/images/tray_active_macos.svg"); - trayIconFile.setIsMask(true); -#else - QIcon trayIconFile = - QIcon(":/res/images/tray_active.png"); -#endif - trayIcon->setIcon(QIcon::fromTheme("obs-tray-active", - trayIconFile)); - } if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index f68075ba3013ee..2da8b76fb6e16c 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -58,6 +58,7 @@ class QListWidgetItem; class VolControl; class OBSBasicStats; class OBSBasicVCamConfig; +class OBSSystemTrayIcon; #include "ui_OBSBasic.h" #include "ui_ColorSelect.h" @@ -199,6 +200,7 @@ class OBSBasic : public OBSMainWindow { friend struct BasicOutputHandler; friend struct OBSStudioAPI; friend class ScreenshotObj; + friend class OBSSystemTrayIcon; enum class MoveDir { Up, Down, Left, Right }; @@ -324,14 +326,7 @@ class OBSBasic : public OBSMainWindow { bool vcamEnabled = false; VCamConfig vcamConfig; - QScopedPointer trayIcon; - QPointer sysTrayStream; - QPointer sysTrayRecord; - QPointer sysTrayReplayBuffer; - QPointer sysTrayVirtualCam; - QPointer showHide; - QPointer exit; - QPointer trayMenu; + QScopedPointer trayIcon; QPointer previewProjector; QPointer studioProgramProjector; QPointer previewProjectorSource; @@ -785,7 +780,6 @@ private slots: void SetBlendingMethod(); void SetBlendingMode(); - void IconActivated(QSystemTrayIcon::ActivationReason reason); void SetShowing(bool showing); void ToggleShowHide(); @@ -1292,6 +1286,9 @@ public slots: /* Studio Mode signal */ void PreviewProgramModeChanged(bool enabled); + /* Window visible signal */ + void VisibilityChanged(bool visible); + private: std::unique_ptr ui;