UI: Add system tray capability

This adds a system tray when enabled. There is also a option to minimize
to the system tray when the app is started.

Closes jp9000/obs-studio#595
This commit is contained in:
cg2121 2016-08-13 09:36:17 -05:00 committed by jp9000
parent 276e8530d9
commit b71f5cc4fc
11 changed files with 298 additions and 22 deletions

View file

@ -383,6 +383,8 @@ Basic.Settings.General.SourceSnapping="Snap Sources to other sources"
Basic.Settings.General.SnapDistance="Snap Sensitivity"
Basic.Settings.General.RecordWhenStreaming="Automatically record when streaming"
Basic.Settings.General.KeepRecordingWhenStreamStops="Keep recording when stream stops"
Basic.Settings.General.SysTrayEnabled="Enable system tray icon"
Basic.Settings.General.SysTrayWhenStarted="Minimize to system tray when started"
# basic mode 'stream' settings
Basic.Settings.Stream="Stream"
@ -550,6 +552,13 @@ Basic.Hotkeys.StartRecording="Start Recording"
Basic.Hotkeys.StopRecording="Stop Recording"
Basic.Hotkeys.SelectScene="Switch to scene"
# system tray
Basic.SystemTray.Show="Show"
Basic.SystemTray.Hide="Hide"
# system tray messages
Basic.SystemTray.Message.Reconnecting="Disconnected. Reconnecting..."
# hotkeys that may lack translation on certain operating systems
Hotkeys.Insert="Insert"
Hotkeys.Delete="Delete"

View file

@ -192,14 +192,31 @@
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<item row="8" column="1">
<widget class="QCheckBox" name="systemTrayEnabled">
<property name="text">
<string>Basic.Settings.General.SysTrayEnabled</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="systemTrayWhenStarted">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Basic.Settings.General.SysTrayWhenStarted</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="11" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_10">
<property name="enabled">
<bool>true</bool>
@ -3753,5 +3770,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>systemTrayEnabled</sender>
<signal>toggled(bool)</signal>
<receiver>systemTrayWhenStarted</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>404</x>
<y>245</y>
</hint>
<hint type="destinationlabel">
<x>404</x>
<y>271</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -13,6 +13,7 @@
<file>images/properties.png</file>
<file>images/up.png</file>
<file>images/obs.png</file>
<file>images/tray_active.png</file>
</qresource>
<qresource prefix="/settings">
<file>images/settings/advanced.png</file>

View file

@ -360,6 +360,10 @@ bool OBSApp::InitGlobalConfigDefaults()
"RecordWhenStreaming", false);
config_set_default_bool(globalConfig, "BasicWindow",
"KeepRecordingWhenStreamStops", false);
config_set_default_bool(globalConfig, "BasicWindow",
"SysTrayEnabled", true);
config_set_default_bool(globalConfig, "BasicWindow",
"SysTrayWhenStarted", false);
config_set_default_bool(globalConfig, "BasicWindow",
"ShowTransitions", true);
config_set_default_bool(globalConfig, "BasicWindow",

View file

@ -604,9 +604,13 @@ bool SimpleOutput::StartRecording()
os_dir_t *dir = path ? os_opendir(path) : nullptr;
if (!dir) {
QMessageBox::information(main,
QTStr("Output.BadPath.Title"),
QTStr("Output.BadPath.Text"));
if (main->isVisible())
QMessageBox::information(main,
QTStr("Output.BadPath.Title"),
QTStr("Output.BadPath.Text"));
else
main->SysTrayNotify(QTStr("Output.BadPath.Text"),
QSystemTrayIcon::Warning);
return false;
}
@ -1147,9 +1151,13 @@ bool AdvancedOutput::StartRecording()
os_dir_t *dir = path ? os_opendir(path) : nullptr;
if (!dir) {
QMessageBox::information(main,
QTStr("Output.BadPath.Title"),
QTStr("Output.BadPath.Text"));
if (main->isVisible())
QMessageBox::information(main,
QTStr("Output.BadPath.Title"),
QTStr("Output.BadPath.Text"));
else
main->SysTrayNotify(QTStr("Output.BadPath.Text"),
QSystemTrayIcon::Warning);
return false;
}

View file

@ -1181,6 +1181,8 @@ void OBSBasic::OBSInit()
}
ui->mainSplitter->setSizes(defSizes);
SystemTray(true);
}
void OBSBasic::InitHotkeys()
@ -1447,8 +1449,11 @@ OBSBasic::~OBSBasic()
QList<int> splitterSizes = ui->mainSplitter->sizes();
bool alwaysOnTop = IsAlwaysOnTop(this);
config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry",
saveGeometry().toBase64().constData());
if (isVisible())
config_set_string(App()->GlobalConfig(),
"BasicWindow", "geometry",
saveGeometry().toBase64().constData());
config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterTop",
splitterSizes[0]);
config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterBottom",
@ -2610,6 +2615,8 @@ void OBSBasic::closeEvent(QCloseEvent *event)
blog(LOG_INFO, SHUTDOWN_SEPARATOR);
if (outputHandler && outputHandler->Active()) {
SetShowing(true);
QMessageBox::StandardButton button = QMessageBox::question(
this, QTStr("ConfirmExit.Title"),
QTStr("ConfirmExit.Text"));
@ -2668,6 +2675,7 @@ void OBSBasic::on_action_Settings_triggered()
{
OBSBasicSettings settings(this);
settings.exec();
SystemTray(false);
}
void OBSBasic::on_actionAdvAudioProperties_triggered()
@ -3533,10 +3541,14 @@ void OBSBasic::StartStreaming()
ui->streamButton->setEnabled(false);
ui->streamButton->setText(QTStr("Basic.Main.Connecting"));
sysTrayStream->setEnabled(false);
sysTrayStream->setText(ui->streamButton->text());
if (!outputHandler->StartStreaming(service)) {
ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
ui->streamButton->setEnabled(true);
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
}
bool recordWhenStreaming = config_get_bool(GetGlobalConfig(),
@ -3572,6 +3584,8 @@ inline void OBSBasic::OnActivate()
ui->profileMenu->setEnabled(false);
App()->IncrementSleepInhibition();
UpdateProcessPriority();
trayIcon->setIcon(QIcon(":/res/images/tray_active.png"));
}
}
@ -3581,6 +3595,8 @@ inline void OBSBasic::OnDeactivate()
ui->profileMenu->setEnabled(true);
App()->DecrementSleepInhibition();
ClearProcessPriority();
trayIcon->setIcon(QIcon(":/res/images/obs.png"));
}
}
@ -3622,6 +3638,8 @@ void OBSBasic::StreamDelayStarting(int sec)
{
ui->streamButton->setText(QTStr("Basic.Main.StopStreaming"));
ui->streamButton->setEnabled(true);
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
if (!startStreamMenu.isNull())
startStreamMenu->deleteLater();
@ -3642,6 +3660,8 @@ void OBSBasic::StreamDelayStopping(int sec)
{
ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
ui->streamButton->setEnabled(true);
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
if (!startStreamMenu.isNull())
startStreamMenu->deleteLater();
@ -3661,6 +3681,8 @@ void OBSBasic::StreamingStart()
ui->streamButton->setText(QTStr("Basic.Main.StopStreaming"));
ui->streamButton->setEnabled(true);
ui->statusbar->StreamStarted(outputHandler->streamOutput);
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
OnActivate();
@ -3670,6 +3692,7 @@ void OBSBasic::StreamingStart()
void OBSBasic::StreamStopping()
{
ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming"));
sysTrayStream->setText(ui->streamButton->text());
}
void OBSBasic::StreamingStop(int code)
@ -3704,15 +3727,20 @@ void OBSBasic::StreamingStop(int code)
ui->streamButton->setText(QTStr("Basic.Main.StartStreaming"));
ui->streamButton->setEnabled(true);
sysTrayStream->setText(ui->streamButton->text());
sysTrayStream->setEnabled(true);
OnDeactivate();
blog(LOG_INFO, STREAMING_STOP);
if (code != OBS_OUTPUT_SUCCESS)
if (code != OBS_OUTPUT_SUCCESS && isVisible()) {
QMessageBox::information(this,
QTStr("Output.ConnectFail.Title"),
QT_UTF8(errorMessage));
} else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {
SysTrayNotify(QT_UTF8(errorMessage), QSystemTrayIcon::Warning);
}
if (!startStreamMenu.isNull()) {
ui->streamButton->setMenu(nullptr);
@ -3733,6 +3761,7 @@ void OBSBasic::StartRecording()
void OBSBasic::RecordStopping()
{
ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording"));
sysTrayRecord->setText(ui->recordButton->text());
}
void OBSBasic::StopRecording()
@ -3749,6 +3778,7 @@ void OBSBasic::RecordingStart()
{
ui->statusbar->RecordingStarted(outputHandler->fileOutput);
ui->recordButton->setText(QTStr("Basic.Main.StopRecording"));
sysTrayRecord->setText(ui->recordButton->text());
OnActivate();
@ -3759,22 +3789,35 @@ void OBSBasic::RecordingStop(int code)
{
ui->statusbar->RecordingStopped();
ui->recordButton->setText(QTStr("Basic.Main.StartRecording"));
sysTrayRecord->setText(ui->recordButton->text());
blog(LOG_INFO, RECORDING_STOP);
if (code == OBS_OUTPUT_UNSUPPORTED) {
if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) {
QMessageBox::information(this,
QTStr("Output.RecordFail.Title"),
QTStr("Output.RecordFail.Unsupported"));
} else if (code == OBS_OUTPUT_NO_SPACE) {
} else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) {
QMessageBox::information(this,
QTStr("Output.RecordNoSpace.Title"),
QTStr("Output.RecordNoSpace.Msg"));
} else if (code != OBS_OUTPUT_SUCCESS) {
} else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {
QMessageBox::information(this,
QTStr("Output.RecordError.Title"),
QTStr("Output.RecordError.Msg"));
} else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) {
SysTrayNotify(QTStr("Output.RecordFail.Unsupported"),
QSystemTrayIcon::Warning);
} else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) {
SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"),
QSystemTrayIcon::Warning);
} else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {
SysTrayNotify(QTStr("Output.RecordError.Msg"),
QSystemTrayIcon::Warning);
}
OnDeactivate();
@ -3786,7 +3829,7 @@ void OBSBasic::on_streamButton_clicked()
bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
"WarnBeforeStoppingStream");
if (confirm) {
if (confirm && isVisible()) {
QMessageBox::StandardButton button =
QMessageBox::question(this,
QTStr("ConfirmStop.Title"),
@ -3801,7 +3844,7 @@ void OBSBasic::on_streamButton_clicked()
bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow",
"WarnBeforeStartingStream");
if (confirm) {
if (confirm && isVisible()) {
QMessageBox::StandardButton button =
QMessageBox::question(this,
QTStr("ConfirmStart.Title"),
@ -3827,6 +3870,7 @@ void OBSBasic::on_settingsButton_clicked()
{
OBSBasicSettings settings(this);
settings.exec();
SystemTray(false);
}
void OBSBasic::on_actionWebsite_triggered()
@ -4389,3 +4433,115 @@ void OBSBasic::on_actionLockPreview_triggered()
ui->preview->ToggleLocked();
ui->actionLockPreview->setChecked(ui->preview->Locked());
}
void OBSBasic::SetShowing(bool showing)
{
if (!showing && isVisible()) {
config_set_string(App()->GlobalConfig(),
"BasicWindow", "geometry",
saveGeometry().toBase64().constData());
showHide->setText(QTStr("Basic.SystemTray.Show"));
QTimer::singleShot(250, this, SLOT(hide()));
if (previewEnabled)
EnablePreviewDisplay(false);
setVisible(false);
} else if (showing && !isVisible()) {
showHide->setText(QTStr("Basic.SystemTray.Hide"));
QTimer::singleShot(250, this, SLOT(show()));
if (previewEnabled)
EnablePreviewDisplay(true);
setVisible(true);
}
}
void OBSBasic::SystemTrayInit() {
trayIcon = new QSystemTrayIcon(QIcon(":/res/images/obs.png"),
this);
trayIcon->setToolTip("OBS Studio");
showHide = new QAction(QTStr("Basic.SystemTray.Show"),
trayIcon);
sysTrayStream = new QAction(QTStr("Basic.Main.StartStreaming"),
trayIcon);
sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"),
trayIcon);
exit = new QAction(QTStr("Exit"),
trayIcon);
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(IconActivated(QSystemTrayIcon::ActivationReason)));
connect(showHide, SIGNAL(triggered()),
this, SLOT(ToggleShowHide()));
connect(sysTrayStream, SIGNAL(triggered()),
this, SLOT(on_streamButton_clicked()));
connect(sysTrayRecord, SIGNAL(triggered()),
this, SLOT(on_recordButton_clicked()));
connect(exit, SIGNAL(triggered()),
this, SLOT(close()));
trayMenu = new QMenu;
trayMenu->addAction(showHide);
trayMenu->addAction(sysTrayStream);
trayMenu->addAction(sysTrayRecord);
trayMenu->addAction(exit);
trayIcon->setContextMenu(trayMenu);
}
void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::Trigger)
ToggleShowHide();
}
void OBSBasic::SysTrayNotify(const QString &text,
QSystemTrayIcon::MessageIcon n)
{
if (QSystemTrayIcon::supportsMessages()) {
QSystemTrayIcon::MessageIcon icon =
QSystemTrayIcon::MessageIcon(n);
trayIcon->showMessage("OBS Studio", text, icon, 10000);
}
}
void OBSBasic::SystemTray(bool firstStarted)
{
if (!QSystemTrayIcon::isSystemTrayAvailable())
return;
bool sysTrayWhenStarted = config_get_bool(GetGlobalConfig(),
"BasicWindow", "SysTrayWhenStarted");
bool sysTrayEnabled = config_get_bool(GetGlobalConfig(),
"BasicWindow", "SysTrayEnabled");
if (firstStarted)
SystemTrayInit();
if (!sysTrayWhenStarted && !sysTrayEnabled) {
trayIcon->hide();
} else if (sysTrayWhenStarted && sysTrayEnabled) {
trayIcon->show();
if (firstStarted) {
QTimer::singleShot(50, this, SLOT(hide()));
EnablePreviewDisplay(false);
setVisible(false);
}
} else if (sysTrayEnabled) {
trayIcon->show();
} else if (!sysTrayEnabled) {
trayIcon->hide();
} else if (!sysTrayWhenStarted && sysTrayEnabled) {
trayIcon->hide();
}
if (isVisible())
showHide->setText(QTStr("Basic.SystemTray.Hide"));
else
showHide->setText(QTStr("Basic.SystemTray.Show"));
}

View file

@ -19,6 +19,7 @@
#include <QBuffer>
#include <QAction>
#include <QSystemTrayIcon>
#include <obs.hpp>
#include <vector>
#include <memory>
@ -135,6 +136,14 @@ private:
QPointer<QMenu> startStreamMenu;
QSystemTrayIcon *trayIcon;
QMenu *trayMenu;
QAction *sysTrayStream;
QAction *sysTrayRecord;
QAction *showHide;
QAction *showPreview;
QAction *exit;
void DrawBackdrop(float cx, float cy);
void SetupEncoders();
@ -340,6 +349,14 @@ private slots:
void SetScaleFilter();
void IconActivated(QSystemTrayIcon::ActivationReason reason);
void SetShowing(bool showing);
inline void ToggleShowHide()
{
SetShowing(!isVisible());
}
private:
/* OBS Callbacks */
static void SceneReordered(void *data, calldata_t *params);
@ -366,6 +383,8 @@ private:
public:
OBSScene GetCurrentScene();
void SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n);
inline OBSSource GetCurrentSceneSource()
{
OBSScene curScene = GetCurrentScene();
@ -414,6 +433,9 @@ public:
void UpdateTitleBar();
void UpdateSceneSelection(OBSSource source);
void SystemTrayInit();
void SystemTray(bool firstStarted);
protected:
virtual void closeEvent(QCloseEvent *event) override;
virtual void changeEvent(QEvent *event) override;

View file

@ -274,6 +274,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->recordWhenStreaming, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->keepRecordStreamStops,CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->systemTrayEnabled, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->systemTrayWhenStarted,CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->snappingEnabled, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->screenSnapping, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->centerSnapping, CHECK_CHANGED, GENERAL_CHANGED);
@ -845,6 +847,14 @@ void OBSBasicSettings::LoadGeneralSettings()
"BasicWindow", "KeepRecordingWhenStreamStops");
ui->keepRecordStreamStops->setChecked(keepRecordStreamStops);
bool systemTrayEnabled = config_get_bool(GetGlobalConfig(),
"BasicWindow", "SysTrayEnabled");
ui->systemTrayEnabled->setChecked(systemTrayEnabled);
bool systemTrayWhenStarted = config_get_bool(GetGlobalConfig(),
"BasicWindow", "SysTrayWhenStarted");
ui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted);
bool snappingEnabled = config_get_bool(GetGlobalConfig(),
"BasicWindow", "SnappingEnabled");
ui->snappingEnabled->setChecked(snappingEnabled);
@ -2235,6 +2245,16 @@ void OBSBasicSettings::SaveGeneralSettings()
config_set_bool(GetGlobalConfig(), "BasicWindow",
"KeepRecordingWhenStreamStops",
ui->keepRecordStreamStops->isChecked());
if (WidgetChanged(ui->systemTrayEnabled))
config_set_bool(GetGlobalConfig(), "BasicWindow",
"SysTrayEnabled",
ui->systemTrayEnabled->isChecked());
if (WidgetChanged(ui->systemTrayWhenStarted))
config_set_bool(GetGlobalConfig(), "BasicWindow",
"SysTrayWhenStarted",
ui->systemTrayWhenStarted->isChecked());
}
void OBSBasicSettings::SaveStream1Settings()

View file

@ -72,6 +72,7 @@ void OBSBasicStatusBar::Deactivate()
delaySecStopping = 0;
reconnectTimeout = 0;
active = false;
overloadedNotify = true;
}
}
@ -165,9 +166,10 @@ void OBSBasicStatusBar::UpdateSessionTime()
sessionTime->setMinimumWidth(sessionTime->width());
if (reconnectTimeout > 0) {
QString msg = QTStr("Basic.StatusBar.Reconnecting");
showMessage(msg.arg(QString::number(retries),
QString::number(reconnectTimeout)));
QString msg = QTStr("Basic.StatusBar.Reconnecting")
.arg(QString::number(retries),
QString::number(reconnectTimeout));
showMessage(msg);
reconnectTimeout--;
} else if (retries > 0) {
@ -224,12 +226,20 @@ void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *params
void OBSBasicStatusBar::Reconnect(int seconds)
{
retries++;
OBSBasic *main = qobject_cast<OBSBasic*>(parent());
if (!retries)
main->SysTrayNotify(
QTStr("Basic.SystemTray.Message.Reconnecting"),
QSystemTrayIcon::Warning);
reconnectTimeout = seconds;
if (streamOutput) {
delaySecTotal = obs_output_get_active_delay(streamOutput);
UpdateDelayMsg();
retries++;
}
}
@ -246,7 +256,11 @@ void OBSBasicStatusBar::ReconnectClear()
void OBSBasicStatusBar::ReconnectSuccess()
{
showMessage(QTStr("Basic.StatusBar.ReconnectSuccessful"), 4000);
OBSBasic *main = qobject_cast<OBSBasic*>(parent());
QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
showMessage(msg, 4000);
main->SysTrayNotify(msg, QSystemTrayIcon::Information);
ReconnectClear();
if (streamOutput) {
@ -257,6 +271,8 @@ void OBSBasicStatusBar::ReconnectSuccess()
void OBSBasicStatusBar::UpdateStatusBar()
{
OBSBasic *main = qobject_cast<OBSBasic*>(parent());
UpdateBandwidth();
UpdateSessionTime();
UpdateDroppedFrames();
@ -270,8 +286,14 @@ void OBSBasicStatusBar::UpdateStatusBar()
int diff = skipped - lastSkippedFrameCount;
double percentage = double(skipped) / double(total) * 100.0;
if (diff > 10 && percentage >= 0.1f)
if (diff > 10 && percentage >= 0.1f) {
showMessage(QTStr("HighResourceUsage"), 4000);
if (!main->isVisible() && overloadedNotify) {
main->SysTrayNotify(QTStr("HighResourceUsage"),
QSystemTrayIcon::Warning);
overloadedNotify = false;
}
}
lastSkippedFrameCount = skipped;
}

View file

@ -21,6 +21,7 @@ private:
obs_output_t *streamOutput = nullptr;
obs_output_t *recordOutput = nullptr;
bool active = false;
bool overloadedNotify = true;
int retries = 0;
int totalSeconds = 0;