mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-03-28 03:12:02 -04:00
When no role is set, the default is QAction::TextHeuristicRole. This means that the text of the item gets fuzzy-matched in Qt against a set of possible strings that could indicate that the menu should be in the application menu on macOS. For us this meant however that on some languages, the translation of "WebSocket Server Settings" would begin with "Config", and as such the related QAction replaces our "Settings" action for the PreferencesRole, and clicking "Preferences..." in the application menu would open the websocket settings. It should probably be considered a bug in Qt that implicit matches via TextHeuristicRole can overwrite ones that are explicitly set (like our PreferencesRole). However we explicitly set our roles ourselves anyways and there is no scenario where a plugin should overwrite them, we can just default actions added via the Frontend API to be NoRole; and worry about the Qt bug later.
353 lines
11 KiB
C++
353 lines
11 KiB
C++
/******************************************************************************
|
|
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
|
|
Zachary Lund <admin@computerquip.com>
|
|
Philippe Groarke <philippe.groarke@gmail.com>
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
#include "OBSBasic.hpp"
|
|
|
|
#include <qt-wrappers.hpp>
|
|
|
|
void assignDockToggle(QDockWidget *dock, QAction *action)
|
|
{
|
|
auto handleWindowToggle = [action](bool vis) {
|
|
action->blockSignals(true);
|
|
action->setChecked(vis);
|
|
action->blockSignals(false);
|
|
};
|
|
auto handleMenuToggle = [dock](bool check) {
|
|
dock->blockSignals(true);
|
|
dock->setVisible(check);
|
|
dock->blockSignals(false);
|
|
};
|
|
|
|
dock->connect(dock->toggleViewAction(), &QAction::toggled, handleWindowToggle);
|
|
dock->connect(action, &QAction::toggled, handleMenuToggle);
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* prune deleted extra docks */
|
|
for (int i = oldExtraDocks.size() - 1; i >= 0; i--) {
|
|
if (!oldExtraDocks[i]) {
|
|
oldExtraDocks.removeAt(i);
|
|
oldExtraDockNames.removeAt(i);
|
|
}
|
|
}
|
|
|
|
#ifdef BROWSER_AVAILABLE
|
|
if ((oldExtraDocks.size() || extraDocks.size() || extraCustomDocks.size() || extraBrowserDocks.size()) &&
|
|
!force)
|
|
#else
|
|
if ((oldExtraDocks.size() || extraDocks.size() || extraCustomDocks.size()) && !force)
|
|
#endif
|
|
{
|
|
QMessageBox::StandardButton button =
|
|
OBSMessageBox::question(this, QTStr("ResetUIWarning.Title"), QTStr("ResetUIWarning.Text"));
|
|
|
|
if (button == QMessageBox::No)
|
|
return;
|
|
}
|
|
|
|
/* undock/hide/center extra docks */
|
|
for (int i = oldExtraDocks.size() - 1; i >= 0; i--) {
|
|
if (oldExtraDocks[i]) {
|
|
oldExtraDocks[i]->setVisible(true);
|
|
oldExtraDocks[i]->setFloating(true);
|
|
oldExtraDocks[i]->move(frameGeometry().topLeft() + rect().center() -
|
|
oldExtraDocks[i]->rect().center());
|
|
oldExtraDocks[i]->setVisible(false);
|
|
}
|
|
}
|
|
|
|
#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<QDockWidget *> docks{ui->scenesDock, ui->sourcesDock, ui->mixerDock, ui->transitionsDock, controlsDock};
|
|
|
|
QList<int> 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
|
|
|
|
for (int i = oldExtraDocks.size() - 1; i >= 0; i--) {
|
|
if (!oldExtraDocks[i]) {
|
|
oldExtraDocks.removeAt(i);
|
|
oldExtraDockNames.removeAt(i);
|
|
} else {
|
|
oldExtraDocks[i]->setFeatures(features);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (!extraBrowserMenuDocksSeparator.isNull())
|
|
ui->menuDocks->insertAction(extraBrowserMenuDocksSeparator, action);
|
|
else
|
|
ui->menuDocks->addAction(action);
|
|
#else
|
|
QAction *action = ui->menuDocks->addAction(dock->windowTitle());
|
|
#endif
|
|
action->setCheckable(true);
|
|
action->setMenuRole(QAction::NoRole);
|
|
assignDockToggle(dock, action);
|
|
oldExtraDocks.push_back(dock);
|
|
oldExtraDockNames.push_back(dock->objectName());
|
|
|
|
bool lock = ui->lockDocks->isChecked();
|
|
QDockWidget::DockWidgetFeatures features =
|
|
lock ? QDockWidget::NoDockWidgetFeatures
|
|
: (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |
|
|
QDockWidget::DockWidgetFloatable);
|
|
|
|
dock->setFeatures(features);
|
|
|
|
/* prune deleted docks */
|
|
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<QDockWidget *>(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)
|
|
{
|
|
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<QDockWidget>(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 << 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<QDockWidget *>(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]);
|
|
}
|