diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 81d72f809..c09e8e39d 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -250,6 +250,7 @@ Basic.MainMenu.File.Remux="Re&mux Recordings" Basic.MainMenu.File.Settings="&Settings" Basic.MainMenu.File.ShowSettingsFolder="Show Settings Folder" Basic.MainMenu.File.ShowProfileFolder="Show Profile Folder" +Basic.MainMenu.AlwaysOnTop="&Always On Top" Basic.MainMenu.File.Exit="E&xit" # basic mode edit menu diff --git a/obs/forms/OBSBasic.ui b/obs/forms/OBSBasic.ui index 8ca9bae68..9449a8549 100644 --- a/obs/forms/OBSBasic.ui +++ b/obs/forms/OBSBasic.ui @@ -542,6 +542,8 @@ + + @@ -975,6 +977,14 @@ Basic.MainMenu.File.ShowProfileFolder + + + true + + + Basic.MainMenu.AlwaysOnTop + + diff --git a/obs/platform-osx.mm b/obs/platform-osx.mm index a230cd99a..8f68ea43f 100644 --- a/obs/platform-osx.mm +++ b/obs/platform-osx.mm @@ -130,3 +130,21 @@ vector GetPreferredLocales() return result; } + +bool IsAlwaysOnTop(QMainWindow *window) +{ + return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0; +} + +void SetAlwaysOnTop(QMainWindow *window, bool enable) +{ + Qt::WindowFlags flags = window->windowFlags(); + + if (enable) + flags |= Qt::WindowStaysOnTopHint; + else + flags &= ~Qt::WindowStaysOnTopHint; + + window->setWindowFlags(flags); + window->show(); +} diff --git a/obs/platform-windows.cpp b/obs/platform-windows.cpp index 3d624909b..c34bbd565 100644 --- a/obs/platform-windows.cpp +++ b/obs/platform-windows.cpp @@ -202,3 +202,16 @@ void SetAeroEnabled(bool enable) func(enable ? DWM_EC_ENABLECOMPOSITION : DWM_EC_DISABLECOMPOSITION); } + +bool IsAlwaysOnTop(QMainWindow *window) +{ + DWORD exStyle = GetWindowLong((HWND)window->winId(), GWL_EXSTYLE); + return (exStyle & WS_EX_TOPMOST) != 0; +} + +void SetAlwaysOnTop(QMainWindow *window, bool enable) +{ + HWND hwnd = (HWND)window->winId(); + SetWindowPos(hwnd, enable ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); +} diff --git a/obs/platform-x11.cpp b/obs/platform-x11.cpp index 424a8f78f..582d626d3 100644 --- a/obs/platform-x11.cpp +++ b/obs/platform-x11.cpp @@ -147,3 +147,21 @@ vector GetPreferredLocales() return {}; } + +bool IsAlwaysOnTop(QMainWindow *window) +{ + return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0; +} + +void SetAlwaysOnTop(QMainWindow *window, bool enable) +{ + Qt::WindowFlags flags = window->windowFlags(); + + if (enable) + flags |= Qt::WindowStaysOnTopHint; + else + flags &= ~Qt::WindowStaysOnTopHint; + + window->setWindowFlags(flags); + window->show(); +} diff --git a/obs/platform.hpp b/obs/platform.hpp index 30a6ae33d..5f1658910 100644 --- a/obs/platform.hpp +++ b/obs/platform.hpp @@ -22,6 +22,8 @@ #include #include +class QMainWindow; + struct MonitorInfo { int32_t x, y; uint32_t cx, cy; @@ -43,6 +45,9 @@ std::string GetDefaultVideoSavePath(); std::vector GetPreferredLocales(); +bool IsAlwaysOnTop(QMainWindow *window); +void SetAlwaysOnTop(QMainWindow *window, bool enable); + #ifdef _WIN32 uint32_t GetWindowsVersion(); void SetAeroEnabled(bool enable); diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index d0994ac6a..d944febaa 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -928,7 +928,20 @@ void OBSBasic::OBSInit() connect(ui->preview, &OBSQTDisplay::DisplayCreated, addDisplay); +#ifdef _WIN32 show(); +#endif + + bool alwaysOnTop = config_get_bool(App()->GlobalConfig(), "BasicWindow", + "AlwaysOnTop"); + if (alwaysOnTop) { + SetAlwaysOnTop(this, true); + ui->actionAlwaysOnTop->setChecked(true); + } + +#ifndef _WIN32 + show(); +#endif QList defSizes; @@ -1177,6 +1190,7 @@ OBSBasic::~OBSBasic() QRect lastGeom = normalGeometry(); QList splitterSizes = ui->mainSplitter->sizes(); + bool alwaysOnTop = IsAlwaysOnTop(this); config_set_int(App()->GlobalConfig(), "BasicWindow", "cx", lastGeom.width()); @@ -1192,6 +1206,8 @@ OBSBasic::~OBSBasic() splitterSizes[1]); config_set_bool(App()->GlobalConfig(), "BasicWindow", "PreviewEnabled", previewEnabled); + config_set_bool(App()->GlobalConfig(), "BasicWindow", "AlwaysOnTop", + alwaysOnTop); config_save_safe(App()->GlobalConfig(), "tmp", nullptr); #ifdef _WIN32 @@ -3326,6 +3342,28 @@ void OBSBasic::on_previewDisabledLabel_customContextMenuRequested( UNUSED_PARAMETER(pos); } +void OBSBasic::on_actionAlwaysOnTop_triggered() +{ + CloseDialogs(); + + /* Make sure all dialogs are safely and successfully closed before + * switching the always on top mode due to the fact that windows all + * have to be recreated, so queue the actual toggle to happen after + * all events related to closing the dialogs have finished */ + QMetaObject::invokeMethod(this, "ToggleAlwaysOnTop", + Qt::QueuedConnection); +} + +void OBSBasic::ToggleAlwaysOnTop() +{ + bool isAlwaysOnTop = IsAlwaysOnTop(this); + + ui->actionAlwaysOnTop->setChecked(!isAlwaysOnTop); + SetAlwaysOnTop(this, !isAlwaysOnTop); + + show(); +} + void OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const { const char *val = config_get_string(basicConfig, "Video", "FPSCommon"); diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 6d84f9db8..fb2eda4e6 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -229,6 +229,8 @@ private slots: void RemoveSelectedScene(); void RemoveSelectedSceneItem(); + void ToggleAlwaysOnTop(); + void ReorderSources(OBSScene scene); void ProcessHotkey(obs_hotkey_id id, bool pressed); @@ -377,6 +379,8 @@ private slots: void on_actionShowSettingsFolder_triggered(); void on_actionShowProfileFolder_triggered(); + void on_actionAlwaysOnTop_triggered(); + void logUploadFinished(const QString &text, const QString &error); void updateFileFinished(const QString &text, const QString &error);