From aaa47c04915a722d9fa3c0258f232bbc51ccb72c Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 22 Oct 2022 09:49:27 +0200 Subject: [PATCH 1/3] UI: Avoid having two dock with the same object name https://doc.qt.io/qt-6/qmainwindow.html#saveState You should make sure that this property is unique for each QToolBar and QDockWidget you add to the QMainWindow. --- UI/api-interface.cpp | 18 +++++++++++++++++- UI/window-basic-main.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ UI/window-basic-main.hpp | 4 ++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 800788be7ec8aa..e6f3761742df65 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -379,7 +379,23 @@ struct OBSStudioAPI : obs_frontend_callbacks { void *obs_frontend_add_dock(void *dock) override { - return (void *)main->AddDockWidget((QDockWidget *)dock); + QDockWidget *d = reinterpret_cast(dock); + + QString name = d->objectName(); + if (name.isEmpty() || main->IsDockObjectNameUsed(name)) { + blog(LOG_WARNING, + "The object name of the added dock is empty or already used," + " a temporary one will be set to avoid conflicts"); + + char *uuid = os_generate_uuid(); + name = QT_UTF8(uuid); + bfree(uuid); + name.append("_oldExtraDock"); + + d->setObjectName(name); + } + + return (void *)main->AddDockWidget(d); } void obs_frontend_add_event_callback(obs_frontend_event_cb callback, diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index f25fc3251d6f18..5ac1a5a547ca6c 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -9296,6 +9296,7 @@ void OBSBasic::on_resetDocks_triggered(bool force) for (int i = oldExtraDocks.size() - 1; i >= 0; i--) { if (!oldExtraDocks[i]) { oldExtraDocks.removeAt(i); + oldExtraDockNames.removeAt(i); } } @@ -9404,6 +9405,7 @@ void OBSBasic::on_lockDocks_toggled(bool lock) for (int i = oldExtraDocks.size() - 1; i >= 0; i--) { if (!oldExtraDocks[i]) { oldExtraDocks.removeAt(i); + oldExtraDockNames.removeAt(i); } else { oldExtraDocks[i]->setFeatures(features); } @@ -10196,6 +10198,10 @@ void OBSBasic::ResizeOutputSizeOfSource() QAction *OBSBasic::AddDockWidget(QDockWidget *dock) { + // Prevent the object name from being changed + connect(dock, &QObject::objectNameChanged, this, + &OBSBasic::RepairOldExtraDockName); + #ifdef BROWSER_AVAILABLE QAction *action = new QAction(dock->windowTitle(), ui->menuDocks); @@ -10210,6 +10216,7 @@ QAction *OBSBasic::AddDockWidget(QDockWidget *dock) action->setCheckable(true); assignDockToggle(dock, action); oldExtraDocks.push_back(dock); + oldExtraDockNames.push_back(dock->objectName()); bool lock = ui->lockDocks->isChecked(); QDockWidget::DockWidgetFeatures features = @@ -10224,12 +10231,30 @@ QAction *OBSBasic::AddDockWidget(QDockWidget *dock) for (int i = oldExtraDocks.size() - 1; i >= 0; i--) { if (!oldExtraDocks[i]) { oldExtraDocks.removeAt(i); + oldExtraDockNames.removeAt(i); } } return action; } +void OBSBasic::RepairOldExtraDockName() +{ + QDockWidget *dock = reinterpret_cast(sender()); + int idx = oldExtraDocks.indexOf(dock); + QSignalBlocker block(dock); + + if (idx == -1) { + blog(LOG_WARNING, "A dock got its object name changed"); + return; + } + + blog(LOG_WARNING, "The dock '%s' got its object name restored", + QT_TO_UTF8(oldExtraDockNames[idx])); + + dock->setObjectName(oldExtraDockNames[idx]); +} + void OBSBasic::AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, bool extraBrowser) { @@ -10280,6 +10305,21 @@ void OBSBasic::RemoveDockWidget(const QString &name) extraDocks.removeAt(idx); } +bool OBSBasic::IsDockObjectNameUsed(const QString &name) +{ + QStringList list; + list << "scenesDock" + << "sourcesDock" + << "mixerDock" + << "transitionsDock" + << "controlsDock" + << "statsDock"; + list << oldExtraDockNames; + list << extraDockNames; + + return list.contains(name); +} + OBSBasic *OBSBasic::Get() { return reinterpret_cast(App()->GetMainWindow()); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 86509213df7ee3..f02abe6cd70f98 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -229,6 +229,7 @@ class OBSBasic : public OBSMainWindow { std::vector signalHandlers; QList> oldExtraDocks; + QStringList oldExtraDockNames; bool loaded = false; long disableSaving = 1; @@ -970,6 +971,7 @@ private slots: void AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, bool extraBrowser = false); void RemoveDockWidget(const QString &name); + bool IsDockObjectNameUsed(const QString &name); static OBSBasic *Get(); @@ -1201,6 +1203,8 @@ private slots: void ResizeOutputSizeOfSource(); + void RepairOldExtraDockName(); + public slots: void on_actionResetTransform_triggered(); From 985f2c59c4125f2873935d59f3c16dd733696a15 Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 22 Oct 2022 10:36:53 +0200 Subject: [PATCH 2/3] obs-frontend-api,UI,docs: Add dock API that ask for unique object name obs_frontend_add_dock() is deprecated in favor of obs_frontend_add_dock_by_id() --- UI/api-interface.cpp | 29 +++++++++++++++++++ UI/obs-frontend-api/obs-frontend-api.cpp | 14 +++++++++ UI/obs-frontend-api/obs-frontend-api.h | 7 +++++ UI/obs-frontend-api/obs-frontend-internal.hpp | 5 ++++ docs/sphinx/reference-frontend-api.rst | 27 +++++++++++++++++ 5 files changed, 82 insertions(+) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index e6f3761742df65..31e9a9632c9072 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -398,6 +398,35 @@ struct OBSStudioAPI : obs_frontend_callbacks { return (void *)main->AddDockWidget(d); } + bool obs_frontend_add_dock_by_id(const char *id, const char *title, + void *widget) override + { + if (main->IsDockObjectNameUsed(QT_UTF8(id))) { + blog(LOG_WARNING, + "Dock id '%s' already used! " + "Duplicate library?", + id); + return false; + } + + OBSDock *dock = new OBSDock(main); + dock->setWidget((QWidget *)widget); + dock->setWindowTitle(QT_UTF8(title)); + dock->setObjectName(QT_UTF8(id)); + + main->AddDockWidget(dock, Qt::RightDockWidgetArea); + + dock->setFloating(true); + dock->setVisible(false); + + return true; + } + + void obs_frontend_remove_dock(const char *id) override + { + main->RemoveDockWidget(QT_UTF8(id)); + } + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override { diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 56b349da4a3fa4..15e1ac685bc34f 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -330,6 +330,20 @@ void *obs_frontend_add_dock(void *dock) return !!callbacks_valid() ? c->obs_frontend_add_dock(dock) : nullptr; } +bool obs_frontend_add_dock_by_id(const char *id, const char *title, + void *widget) +{ + return !!callbacks_valid() + ? c->obs_frontend_add_dock_by_id(id, title, widget) + : false; +} + +void obs_frontend_remove_dock(const char *id) +{ + if (callbacks_valid()) + c->obs_frontend_remove_dock(id); +} + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) { diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index df00024c2dde00..4102ea3f1ac6cc 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -138,8 +138,15 @@ EXPORT void obs_frontend_add_tools_menu_item(const char *name, void *private_data); /* takes QDockWidget and returns QAction */ +OBS_DEPRECATED EXPORT void *obs_frontend_add_dock(void *dock); +/* takes QWidget for widget */ +EXPORT bool obs_frontend_add_dock_by_id(const char *id, const char *title, + void *widget); + +EXPORT void obs_frontend_remove_dock(const char *id); + typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index e0cd9a7b718692..58ba51ae91b04c 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -66,6 +66,11 @@ struct obs_frontend_callbacks { virtual void *obs_frontend_add_dock(void *dock) = 0; + virtual bool obs_frontend_add_dock_by_id(const char *id, + const char *title, + void *widget) = 0; + virtual void obs_frontend_remove_dock(const char *id) = 0; + virtual void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) = 0; diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index 4e5e89118e7c9d..b4ed0f4d4ad843 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -447,6 +447,33 @@ Functions :param dock: QDockWidget to add/create :return: A pointer to the added QAction +.. deprecated:: 29.1 + Prefer :c:func:`obs_frontend_add_dock_by_id()` instead. + +--------------------------------------- + +.. function:: bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) + + Adds a dock with the widget to the UI with a toggle in the Docks + menu. + + Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock + and the id from the UI. + + :param id: Unique identifier of the dock + :param title: Window title of the dock + :param widget: QWidget to insert in the dock + :return: *true* if the dock was added, *false* if the id was already + used + +--------------------------------------- + +.. function:: void obs_frontend_remove_dock(const char *id) + + Removes the dock with this id from the UI. + + :param id: Unique identifier of the dock to remove. + --------------------------------------- .. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) From 92f1c4ab9ff448e0a3ffa8dfc4ae71661e588d5e Mon Sep 17 00:00:00 2001 From: tytan652 Date: Sat, 22 Oct 2022 11:51:30 +0200 Subject: [PATCH 3/3] obs-frontend-api,UI,docs: Add API to add custom docks with no toggle Some plugin does that by deleting the QAction returned by obs_frontend_add_dock(). Now that obs_frontend_add_dock() is deprecated, obs_frontend_add_custom_qdock() replace this usage. --- UI/api-interface.cpp | 18 +++++ UI/obs-frontend-api/obs-frontend-api.cpp | 6 ++ UI/obs-frontend-api/obs-frontend-api.h | 3 + UI/obs-frontend-api/obs-frontend-internal.hpp | 2 + UI/window-basic-main.cpp | 66 ++++++++++++++++--- UI/window-basic-main.hpp | 5 ++ docs/sphinx/reference-frontend-api.rst | 17 ++++- 7 files changed, 107 insertions(+), 10 deletions(-) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp index 31e9a9632c9072..8fb3bea3a3816d 100644 --- a/UI/api-interface.cpp +++ b/UI/api-interface.cpp @@ -427,6 +427,24 @@ struct OBSStudioAPI : obs_frontend_callbacks { main->RemoveDockWidget(QT_UTF8(id)); } + bool obs_frontend_add_custom_qdock(const char *id, void *dock) override + { + if (main->IsDockObjectNameUsed(QT_UTF8(id))) { + blog(LOG_WARNING, + "Dock id '%s' already used! " + "Duplicate library?", + id); + return false; + } + + QDockWidget *d = reinterpret_cast(dock); + d->setObjectName(QT_UTF8(id)); + + main->AddCustomDockWidget(d); + + return true; + } + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override { diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp index 15e1ac685bc34f..34747fc07b5ab1 100644 --- a/UI/obs-frontend-api/obs-frontend-api.cpp +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -344,6 +344,12 @@ void obs_frontend_remove_dock(const char *id) c->obs_frontend_remove_dock(id); } +bool obs_frontend_add_custom_qdock(const char *id, void *dock) +{ + return !!callbacks_valid() ? c->obs_frontend_add_custom_qdock(id, dock) + : false; +} + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) { diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h index 4102ea3f1ac6cc..a0913e683359f1 100644 --- a/UI/obs-frontend-api/obs-frontend-api.h +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -147,6 +147,9 @@ EXPORT bool obs_frontend_add_dock_by_id(const char *id, const char *title, EXPORT void obs_frontend_remove_dock(const char *id); +/* takes QDockWidget for dock */ +EXPORT bool obs_frontend_add_custom_qdock(const char *id, void *dock); + typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data); diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp index 58ba51ae91b04c..90a0e06ea9dd5b 100644 --- a/UI/obs-frontend-api/obs-frontend-internal.hpp +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -70,6 +70,8 @@ struct obs_frontend_callbacks { const char *title, void *widget) = 0; virtual void obs_frontend_remove_dock(const char *id) = 0; + virtual bool obs_frontend_add_custom_qdock(const char *id, + void *dock) = 0; virtual void obs_frontend_add_event_callback(obs_frontend_event_cb callback, diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 5ac1a5a547ca6c..d26d8951a65280 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -9302,10 +9302,12 @@ void OBSBasic::on_resetDocks_triggered(bool force) #ifdef BROWSER_AVAILABLE if ((oldExtraDocks.size() || extraDocks.size() || - extraBrowserDocks.size()) && + extraCustomDocks.size() || extraBrowserDocks.size()) && !force) { #else - if ((oldExtraDocks.size() || extraDocks.size()) && !force) { + if ((oldExtraDocks.size() || extraDocks.size() || + extraCustomDocks.size()) && + !force) { #endif QMessageBox::StandardButton button = QMessageBox::question( this, QTStr("ResetUIWarning.Title"), @@ -9338,6 +9340,7 @@ void OBSBasic::on_resetDocks_triggered(bool force) } RESET_DOCKLIST(extraDocks) + RESET_DOCKLIST(extraCustomDocks) #ifdef BROWSER_AVAILABLE RESET_DOCKLIST(extraBrowserDocks) #endif @@ -9397,6 +9400,9 @@ void OBSBasic::on_lockDocks_toggled(bool lock) for (int i = extraDocks.size() - 1; i >= 0; i--) extraDocks[i]->setFeatures(features); + for (int i = extraCustomDocks.size() - 1; i >= 0; i--) + extraCustomDocks[i]->setFeatures(features); + #ifdef BROWSER_AVAILABLE for (int i = extraBrowserDocks.size() - 1; i >= 0; i--) extraBrowserDocks[i]->setFeatures(features); @@ -10296,13 +10302,17 @@ void OBSBasic::AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, void OBSBasic::RemoveDockWidget(const QString &name) { - if (!extraDockNames.contains(name)) - return; - - int idx = extraDockNames.indexOf(name); - extraDockNames.removeAt(idx); - extraDocks[idx].clear(); - extraDocks.removeAt(idx); + if (extraDockNames.contains(name)) { + int idx = extraDockNames.indexOf(name); + extraDockNames.removeAt(idx); + extraDocks[idx].clear(); + extraDocks.removeAt(idx); + } else if (extraCustomDockNames.contains(name)) { + int idx = extraCustomDockNames.indexOf(name); + extraCustomDockNames.removeAt(idx); + removeDockWidget(extraCustomDocks[idx]); + extraCustomDocks.removeAt(idx); + } } bool OBSBasic::IsDockObjectNameUsed(const QString &name) @@ -10316,10 +10326,48 @@ bool OBSBasic::IsDockObjectNameUsed(const QString &name) << "statsDock"; list << oldExtraDockNames; list << extraDockNames; + list << extraCustomDockNames; return list.contains(name); } +void OBSBasic::AddCustomDockWidget(QDockWidget *dock) +{ + // Prevent the object name from being changed + connect(dock, &QObject::objectNameChanged, this, + &OBSBasic::RepairCustomExtraDockName); + + bool lock = ui->lockDocks->isChecked(); + QDockWidget::DockWidgetFeatures features = + lock ? QDockWidget::NoDockWidgetFeatures + : (QDockWidget::DockWidgetClosable | + QDockWidget::DockWidgetMovable | + QDockWidget::DockWidgetFloatable); + + dock->setFeatures(features); + addDockWidget(Qt::RightDockWidgetArea, dock); + + extraCustomDockNames.push_back(dock->objectName()); + extraCustomDocks.push_back(dock); +} + +void OBSBasic::RepairCustomExtraDockName() +{ + QDockWidget *dock = reinterpret_cast(sender()); + int idx = extraCustomDocks.indexOf(dock); + QSignalBlocker block(dock); + + if (idx == -1) { + blog(LOG_WARNING, "A custom dock got its object name changed"); + return; + } + + blog(LOG_WARNING, "The custom dock '%s' got its object name restored", + QT_TO_UTF8(extraCustomDockNames[idx])); + + dock->setObjectName(extraCustomDockNames[idx]); +} + OBSBasic *OBSBasic::Get() { return reinterpret_cast(App()->GetMainWindow()); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index f02abe6cd70f98..5de1d39e4d23d4 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -557,6 +557,9 @@ class OBSBasic : public OBSMainWindow { QStringList extraDockNames; QList> extraDocks; + QStringList extraCustomDockNames; + QList> extraCustomDocks; + #ifdef BROWSER_AVAILABLE QPointer extraBrowserMenuDocksSeparator; @@ -972,6 +975,7 @@ private slots: bool extraBrowser = false); void RemoveDockWidget(const QString &name); bool IsDockObjectNameUsed(const QString &name); + void AddCustomDockWidget(QDockWidget *dock); static OBSBasic *Get(); @@ -1204,6 +1208,7 @@ private slots: void ResizeOutputSizeOfSource(); void RepairOldExtraDockName(); + void RepairCustomExtraDockName(); public slots: void on_actionResetTransform_triggered(); diff --git a/docs/sphinx/reference-frontend-api.rst b/docs/sphinx/reference-frontend-api.rst index b4ed0f4d4ad843..a68db69ce4f527 100644 --- a/docs/sphinx/reference-frontend-api.rst +++ b/docs/sphinx/reference-frontend-api.rst @@ -448,7 +448,8 @@ Functions :return: A pointer to the added QAction .. deprecated:: 29.1 - Prefer :c:func:`obs_frontend_add_dock_by_id()` instead. + Prefer :c:func:`obs_frontend_add_dock_by_id()` or + :c:func:`obs_frontend_add_custom_qdock()` instead. --------------------------------------- @@ -476,6 +477,20 @@ Functions --------------------------------------- +.. function:: bool obs_frontend_add_custom_qdock(const char *id, void *dock) + + Adds a custom dock to the UI with no toggle. + + Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock + reference and id from the UI. + + :param id: Unique identifier of the dock + :param dock: QDockWidget to add + :return: *true* if the dock was added, *false* if the id was already + used + +--------------------------------------- + .. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) Adds a callback that will be called when a frontend event occurs.