diff --git a/lib/macro/macro-dock-window.cpp b/lib/macro/macro-dock-window.cpp new file mode 100644 index 00000000..d9a69866 --- /dev/null +++ b/lib/macro/macro-dock-window.cpp @@ -0,0 +1,170 @@ +#include "macro-dock-window.hpp" +#include "log-helper.hpp" +#include "plugin-state-helpers.hpp" + +#include + +#include +#include +#include + +namespace advss { + +static std::unordered_map windows; +static std::unordered_map windowGeometries; +static std::mutex mutex; + +static void saveDocks(obs_data_t *obj) +{ + std::lock_guard lock(mutex); + OBSDataAutoRelease data = obs_data_create(); + OBSDataArrayAutoRelease array = obs_data_array_create(); + for (const auto &[name, window] : windows) { + OBSDataAutoRelease dockData = obs_data_create(); + obs_data_set_string(dockData, "name", name.c_str()); + obs_data_set_string(dockData, "geometry", + window->GetWindow() + ->saveState() + .toBase64() + .toStdString() + .c_str()); + obs_data_array_push_back(array, dockData); + } + obs_data_set_array(data, "docks", array); + obs_data_set_obj(obj, "dockWindows", data); +} + +static void restoreDockGeometry() +{ + std::lock_guard lock(mutex); + for (const auto &[name, dock] : windows) { + const auto it = windowGeometries.find(name); + if (it == windowGeometries.end()) { + continue; + } + + dock->GetWindow()->restoreState(it->second); + } +} + +static void loadDocks(obs_data_t *obj) +{ + std::lock_guard lock(mutex); + windowGeometries.clear(); + OBSDataAutoRelease data = obs_data_get_obj(obj, "dockWindows"); + OBSDataArrayAutoRelease array = obs_data_get_array(data, "docks"); + auto size = obs_data_array_count(array); + for (size_t i = 0; i < size; ++i) { + OBSDataAutoRelease dockData = obs_data_array_item(array, i); + const auto name = obs_data_get_string(dockData, "name"); + const auto geometry = QByteArray::fromBase64( + obs_data_get_string(dockData, "geometry")); + windowGeometries[name] = geometry; + } + AddPostLoadStep(restoreDockGeometry); +} + +[[maybe_unused]] static bool _ = []() { + AddPluginInitStep([]() { + AddSaveStep(saveDocks); + AddLoadStep(loadDocks); + }); + return true; +}(); + +MacroDockWindow::MacroDockWindow(const std::string &name) + : QFrame(), + _name(name), + _window(new QMainWindow()) +{ + setFrameShape(QFrame::StyledPanel); + setFrameShadow(QFrame::Sunken); + _window->setDockNestingEnabled(true); + auto layout = new QVBoxLayout; + layout->addWidget(_window); + setLayout(layout); +} + +QWidget *MacroDockWindow::AddMacroDock(QWidget *widget, const QString &title) +{ + auto dock = new QDockWidget(); + dock->setWindowTitle(title); + dock->setWidget(widget); + dock->setVisible(true); + dock->setFeatures(QDockWidget::DockWidgetMovable); + dock->setObjectName(title); + _window->addDockWidget(Qt::RightDockWidgetArea, dock); + return dock; +} + +void MacroDockWindow::RenameMacro(const std::string &oldName, + const std::string &newName) +{ + auto docks = _window->findChildren(); + for (const auto dock : docks) { + if (dock->windowTitle() == QString::fromStdString(oldName)) { + dock->setWindowTitle(QString::fromStdString(newName)); + break; + } + } +} + +void MacroDockWindow::RemoveMacroDock(QWidget *widget) +{ + bool removedDock = false; + auto docks = _window->findChildren(); + for (const auto dock : docks) { + if (dock->widget() == widget) { + _window->removeDockWidget(dock); + dock->deleteLater(); + removedDock = true; + break; + } + } + + if (OBSIsShuttingDown()) { + return; + } + + const bool shouldRemoveDockWindow = docks.isEmpty() || + (removedDock && docks.count() == 1); + if (!shouldRemoveDockWindow) { + return; + } + + std::lock_guard lock(mutex); + auto it = windows.find(_name); + if (it != windows.end()) { + windows.erase(it); + } + + const auto id = "advss-dock-window-" + _name; + obs_frontend_remove_dock(id.c_str()); +} + +QMainWindow *MacroDockWindow::GetWindow() const +{ + return _window; +} + +MacroDockWindow *GetDockWindowByName(const std::string &name) +{ + std::lock_guard lock(mutex); + auto it = windows.find(name); + if (it != windows.end()) { + return it->second; + } + + auto window = new MacroDockWindow(name); + const auto id = "advss-dock-window-" + name; + if (!obs_frontend_add_dock_by_id(id.c_str(), name.c_str(), window)) { + blog(LOG_INFO, "failed to add macro dock window '%s'", + id.c_str()); + return nullptr; + } + + windows[name] = window; + return window; +} + +} // namespace advss diff --git a/lib/macro/macro-dock-window.hpp b/lib/macro/macro-dock-window.hpp new file mode 100644 index 00000000..705c411d --- /dev/null +++ b/lib/macro/macro-dock-window.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include + +#include +#include + +namespace advss { + +class MacroDockWindow : public QFrame { + Q_OBJECT + +public: + MacroDockWindow(const std::string &name); + QWidget *AddMacroDock(QWidget *, const QString &title); + void RenameMacro(const std::string &oldName, + const std::string &newName); + void RemoveMacroDock(QWidget *); + QMainWindow *GetWindow() const; + +private: + std::string _name; + QMainWindow *_window; +}; + +MacroDockWindow *GetDockWindowByName(const std::string &name); + +} // namespace advss