/****************************************************************************** Copyright (C) 2023 by Lain Bailey Zachary Lund Philippe Groarke This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "OBSBasic.hpp" #include void setupDockAction(QDockWidget *dock) { QAction *action = dock->toggleViewAction(); auto neverDisable = [action]() { QSignalBlocker block(action); action->setEnabled(true); }; auto newToggleView = [dock](bool check) { QSignalBlocker block(dock); dock->setVisible(check); }; // Replace the slot connected by default QObject::disconnect(action, &QAction::triggered, nullptr, 0); dock->connect(action, &QAction::triggered, newToggleView); // Make the action unable to be disabled action->connect(action, &QAction::enabledChanged, neverDisable); } void OBSBasic::on_resetDocks_triggered(bool force) { #ifdef BROWSER_AVAILABLE if ((extraDocks.size() || extraCustomDocks.size() || extraBrowserDocks.size()) && !force) #else if ((extraDocks.size() || extraCustomDocks.size()) && !force) #endif { QMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr("ResetUIWarning.Title"), QTStr("ResetUIWarning.Text")); if (button == QMessageBox::No) return; } #define RESET_DOCKLIST(dockList) \ for (int i = dockList.size() - 1; i >= 0; i--) { \ dockList[i]->setVisible(true); \ dockList[i]->setFloating(true); \ dockList[i]->move(frameGeometry().topLeft() + rect().center() - dockList[i]->rect().center()); \ dockList[i]->setVisible(false); \ } RESET_DOCKLIST(extraDocks) RESET_DOCKLIST(extraCustomDocks) #ifdef BROWSER_AVAILABLE RESET_DOCKLIST(extraBrowserDocks) #endif #undef RESET_DOCKLIST restoreState(startingDockLayout); int cx = width(); int cy = height(); int cx22_5 = cx * 225 / 1000; int cx5 = cx * 5 / 100; int cx21 = cx * 21 / 100; cy = cy * 225 / 1000; int mixerSize = cx - (cx22_5 * 2 + cx5 + cx21); QList docks{ui->scenesDock, ui->sourcesDock, ui->mixerDock, ui->transitionsDock, controlsDock}; QList sizes{cx22_5, cx22_5, mixerSize, cx5, cx21}; ui->scenesDock->setVisible(true); ui->sourcesDock->setVisible(true); ui->mixerDock->setVisible(true); ui->transitionsDock->setVisible(true); controlsDock->setVisible(true); statsDock->setVisible(false); statsDock->setFloating(true); resizeDocks(docks, {cy, cy, cy, cy, cy}, Qt::Vertical); resizeDocks(docks, sizes, Qt::Horizontal); activateWindow(); } void OBSBasic::on_lockDocks_toggled(bool lock) { QDockWidget::DockWidgetFeatures features = lock ? QDockWidget::NoDockWidgetFeatures : (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); QDockWidget::DockWidgetFeatures mainFeatures = features; mainFeatures &= ~QDockWidget::QDockWidget::DockWidgetClosable; ui->scenesDock->setFeatures(mainFeatures); ui->sourcesDock->setFeatures(mainFeatures); ui->mixerDock->setFeatures(mainFeatures); ui->transitionsDock->setFeatures(mainFeatures); controlsDock->setFeatures(mainFeatures); statsDock->setFeatures(features); 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); #endif } void OBSBasic::on_sideDocks_toggled(bool side) { if (side) { setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); } else { setCorner(Qt::TopLeftCorner, Qt::TopDockWidgetArea); setCorner(Qt::TopRightCorner, Qt::TopDockWidgetArea); setCorner(Qt::BottomLeftCorner, Qt::BottomDockWidgetArea); setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); } } void OBSBasic::AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, bool extraBrowser) { if (dock->objectName().isEmpty()) return; bool lock = ui->lockDocks->isChecked(); QDockWidget::DockWidgetFeatures features = lock ? QDockWidget::NoDockWidgetFeatures : (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); setupDockAction(dock); dock->setFeatures(features); addDockWidget(area, dock); #ifdef BROWSER_AVAILABLE if (extraBrowser && extraBrowserMenuDocksSeparator.isNull()) extraBrowserMenuDocksSeparator = ui->menuDocks->addSeparator(); if (!extraBrowser && !extraBrowserMenuDocksSeparator.isNull()) ui->menuDocks->insertAction(extraBrowserMenuDocksSeparator, dock->toggleViewAction()); else ui->menuDocks->addAction(dock->toggleViewAction()); if (extraBrowser) return; #else UNUSED_PARAMETER(extraBrowser); ui->menuDocks->addAction(dock->toggleViewAction()); #endif extraDockNames.push_back(dock->objectName()); extraDocks.push_back(std::shared_ptr(dock)); } void OBSBasic::RemoveDockWidget(const QString &name) { if (extraDockNames.contains(name)) { int idx = extraDockNames.indexOf(name); extraDockNames.removeAt(idx); extraDocks[idx].reset(); 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) { QStringList list; list << "scenesDock" << "sourcesDock" << "mixerDock" << "transitionsDock" << "controlsDock" << "statsDock"; 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]); }