mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-22 01:44:49 -05:00
Some checks are pending
debian-build / build (push) Waiting to run
Check locale / ubuntu64 (push) Waiting to run
Push to master / Check Formatting 🔍 (push) Waiting to run
Push to master / Build Project 🧱 (push) Waiting to run
Push to master / Create Release 🛫 (push) Blocked by required conditions
New scene selections would have no canvas selected at all. This would cause issues in places which do not support an empty canvas selection. For example, the "scene changed" check of the scene condition would not work as without any canvas it is not clear which scene to check for.
607 lines
14 KiB
C++
607 lines
14 KiB
C++
#include "scene-selection.hpp"
|
|
#include "obs-module-helper.hpp"
|
|
#include "scene-group.hpp"
|
|
#include "scene-switch-helpers.hpp"
|
|
#include "selection-helpers.hpp"
|
|
#include "source-helpers.hpp"
|
|
#include "ui-helpers.hpp"
|
|
#include "utility.hpp"
|
|
#include "variable.hpp"
|
|
|
|
#include <obs-frontend-api.h>
|
|
|
|
#include <QLayout>
|
|
|
|
namespace advss {
|
|
|
|
constexpr std::string_view typeSaveName = "type";
|
|
constexpr std::string_view nameSaveName = "name";
|
|
constexpr std::string_view selectionSaveName = "sceneSelection";
|
|
constexpr std::string_view canvasSaveName = "canvasSelection";
|
|
|
|
void SceneSelection::Save(obs_data_t *obj) const
|
|
{
|
|
OBSDataAutoRelease data = obs_data_create();
|
|
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
|
|
switch (_type) {
|
|
case Type::SCENE:
|
|
obs_data_set_string(data, nameSaveName.data(),
|
|
GetWeakSourceName(_scene).c_str());
|
|
break;
|
|
case Type::GROUP:
|
|
obs_data_set_string(data, nameSaveName.data(),
|
|
_group->name.c_str());
|
|
break;
|
|
case Type::VARIABLE: {
|
|
auto var = _variable.lock();
|
|
if (!var) {
|
|
break;
|
|
}
|
|
obs_data_set_string(data, nameSaveName.data(),
|
|
var->Name().c_str());
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
obs_data_set_string(data, canvasSaveName.data(),
|
|
GetWeakCanvasName(_canvas).c_str());
|
|
obs_data_set_obj(obj, selectionSaveName.data(), data);
|
|
}
|
|
|
|
static OBSWeakSource getWeakSceneSourceByName(const OBSWeakCanvas &weakCanvas,
|
|
const char *name)
|
|
{
|
|
#if LIBOBS_API_VER < MAKE_SEMANTIC_VERSION(31, 1, 0)
|
|
return GetWeakSourceByName(name);
|
|
#else
|
|
if (!weakCanvas) {
|
|
return nullptr;
|
|
}
|
|
|
|
OBSCanvasAutoRelease canvas = obs_weak_canvas_get_canvas(weakCanvas);
|
|
if (!canvas) {
|
|
return nullptr;
|
|
}
|
|
|
|
OBSSourceAutoRelease source =
|
|
obs_canvas_get_source_by_name(canvas, name);
|
|
OBSWeakSource scene = OBSGetWeakRef(source);
|
|
return scene;
|
|
#endif
|
|
}
|
|
|
|
void SceneSelection::Load(obs_data_t *obj, const char *name,
|
|
const char *typeName)
|
|
{
|
|
// TODO: Remove in future version
|
|
if (!obs_data_has_user_value(obj, selectionSaveName.data())) {
|
|
_type = static_cast<Type>(obs_data_get_int(obj, typeName));
|
|
|
|
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
|
|
_canvas = obs_canvas_get_weak_canvas(mainCanvas);
|
|
obs_weak_canvas_release(_canvas);
|
|
|
|
auto targetName = obs_data_get_string(obj, name);
|
|
switch (_type) {
|
|
case Type::SCENE:
|
|
_scene = getWeakSceneSourceByName(_canvas, targetName);
|
|
break;
|
|
case Type::GROUP:
|
|
_group = GetSceneGroupByName(targetName);
|
|
break;
|
|
case Type::PREVIOUS:
|
|
break;
|
|
case Type::CURRENT:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
OBSDataAutoRelease data =
|
|
obs_data_get_obj(obj, selectionSaveName.data());
|
|
|
|
const char *canvasName =
|
|
obs_data_get_string(data, canvasSaveName.data());
|
|
_canvas = GetWeakCanvasByName(
|
|
obs_data_get_string(data, canvasSaveName.data()));
|
|
if (strcmp(canvasName, "") == 0) {
|
|
OBSCanvasAutoRelease mainCanvas = obs_get_main_canvas();
|
|
_canvas = obs_canvas_get_weak_canvas(mainCanvas);
|
|
obs_weak_canvas_release(_canvas);
|
|
}
|
|
|
|
_type = static_cast<Type>(obs_data_get_int(data, typeSaveName.data()));
|
|
auto targetName = obs_data_get_string(data, nameSaveName.data());
|
|
switch (_type) {
|
|
case Type::SCENE:
|
|
_scene = getWeakSceneSourceByName(_canvas, targetName);
|
|
break;
|
|
case Type::GROUP:
|
|
_group = GetSceneGroupByName(targetName);
|
|
break;
|
|
case Type::PREVIOUS:
|
|
break;
|
|
case Type::CURRENT:
|
|
break;
|
|
case Type::PREVIEW:
|
|
break;
|
|
case Type::VARIABLE:
|
|
_variable = GetWeakVariableByName(targetName);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool isScene(const OBSWeakSource &source)
|
|
{
|
|
OBSSourceAutoRelease s = obs_weak_source_get_source(source);
|
|
return !!obs_scene_from_source(s);
|
|
}
|
|
|
|
void SceneSelection::SetScene(const OBSWeakSource &scene)
|
|
{
|
|
_type = Type::SCENE;
|
|
_scene = scene;
|
|
}
|
|
|
|
OBSWeakSource SceneSelection::GetScene(bool advance) const
|
|
{
|
|
switch (_type) {
|
|
case Type::SCENE:
|
|
return _scene;
|
|
case Type::GROUP:
|
|
if (!_group) {
|
|
return nullptr;
|
|
}
|
|
if (advance) {
|
|
return _group->getNextScene();
|
|
}
|
|
return _group->getCurrentScene();
|
|
case Type::PREVIOUS:
|
|
if (!IsMainCanvas(_canvas)) {
|
|
return nullptr;
|
|
}
|
|
return GetPreviousScene();
|
|
case Type::CURRENT:
|
|
if (IsMainCanvas(_canvas)) {
|
|
return GetCurrentScene();
|
|
} else {
|
|
return GetActiveCanvasScene(_canvas);
|
|
}
|
|
case Type::PREVIEW: {
|
|
OBSSourceAutoRelease s =
|
|
obs_frontend_get_current_preview_scene();
|
|
OBSWeakSourceAutoRelease scene = obs_source_get_weak_source(s);
|
|
return scene.Get();
|
|
}
|
|
case Type::VARIABLE: {
|
|
auto var = _variable.lock();
|
|
if (!var) {
|
|
return nullptr;
|
|
}
|
|
auto scene =
|
|
getWeakSceneSourceByName(_canvas, var->Value().c_str());
|
|
if (isScene(scene)) {
|
|
return scene;
|
|
}
|
|
return nullptr;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::string SceneSelection::ToString(bool resolve) const
|
|
{
|
|
switch (_type) {
|
|
case Type::SCENE:
|
|
return GetWeakSourceName(_scene);
|
|
case Type::GROUP:
|
|
if (_group) {
|
|
if (resolve) {
|
|
return _group->name + "[" +
|
|
GetWeakSourceName(
|
|
_group->getCurrentScene()) +
|
|
"]";
|
|
}
|
|
return _group->name;
|
|
}
|
|
break;
|
|
case Type::PREVIOUS:
|
|
return obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
|
case Type::CURRENT:
|
|
return obs_module_text("AdvSceneSwitcher.selectCurrentScene");
|
|
case Type::PREVIEW:
|
|
return obs_module_text("AdvSceneSwitcher.selectPreviewScene");
|
|
case Type::VARIABLE: {
|
|
auto var = _variable.lock();
|
|
if (!var) {
|
|
return "";
|
|
}
|
|
if (resolve) {
|
|
return var->Name() + "[" + var->Value() + "]";
|
|
}
|
|
return var->Name();
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void SceneSelection::ResolveVariables()
|
|
{
|
|
_scene = GetScene();
|
|
_type = Type::SCENE;
|
|
}
|
|
|
|
static obs_weak_canvas_t *getWeakRefToMainCanvas()
|
|
{
|
|
static auto canvas = obs_get_main_canvas();
|
|
static auto weakCanvas = obs_canvas_get_weak_canvas(canvas);
|
|
[[maybe_unused]] static const bool _ = []() {
|
|
// Let's just hope we don't have to deal with selecting scenes
|
|
// when the OBS main canvas gets deleted and release the
|
|
// references here already to avoid reporting leaks on shutdown
|
|
obs_canvas_release(canvas);
|
|
obs_weak_canvas_release(weakCanvas);
|
|
return true;
|
|
}();
|
|
return weakCanvas;
|
|
}
|
|
|
|
SceneSelection SceneSelectionWidget::CurrentSelection()
|
|
{
|
|
SceneSelection s;
|
|
|
|
s._canvas = _forceMainCanvas ? OBSWeakCanvas(getWeakRefToMainCanvas())
|
|
: _canvas->GetCanvas();
|
|
|
|
const int idx = _scenes->currentIndex();
|
|
const auto name = _scenes->currentText();
|
|
if (idx == -1 || name.isEmpty()) {
|
|
return s;
|
|
}
|
|
|
|
if (idx < _placeholderEndIdx) {
|
|
if (IsCurrentSceneSelected(name)) {
|
|
s._type = SceneSelection::Type::CURRENT;
|
|
}
|
|
if (IsPreviousSceneSelected(name)) {
|
|
s._type = SceneSelection::Type::PREVIOUS;
|
|
}
|
|
if (IsPreviewSceneSelected(name)) {
|
|
s._type = SceneSelection::Type::PREVIEW;
|
|
}
|
|
} else if (idx < _variablesEndIdx) {
|
|
s._type = SceneSelection::Type::VARIABLE;
|
|
s._variable = GetWeakVariableByQString(name);
|
|
} else if (idx < _groupsEndIdx) {
|
|
s._type = SceneSelection::Type::GROUP;
|
|
s._group = GetSceneGroupByQString(name);
|
|
} else if (idx < _scenesEndIdx) {
|
|
s._type = SceneSelection::Type::SCENE;
|
|
s._scene = getWeakSceneSourceByName(s._canvas,
|
|
name.toStdString().c_str());
|
|
}
|
|
return s;
|
|
}
|
|
|
|
static QStringList getOrderList(bool current, bool previous, bool preview)
|
|
{
|
|
QStringList list;
|
|
if (current) {
|
|
list << obs_module_text("AdvSceneSwitcher.selectCurrentScene");
|
|
}
|
|
if (previous) {
|
|
list << obs_module_text("AdvSceneSwitcher.selectPreviousScene");
|
|
}
|
|
if (preview) {
|
|
list << obs_module_text("AdvSceneSwitcher.selectPreviewScene");
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static QStringList getSceneGroupsList()
|
|
{
|
|
QStringList list;
|
|
for (const auto &sg : GetSceneGroups()) {
|
|
list << QString::fromStdString(sg.name);
|
|
}
|
|
list.sort();
|
|
return list;
|
|
}
|
|
|
|
static QStringList getCanvasScenesList(obs_weak_canvas_t *weakCanvas)
|
|
{
|
|
#if LIBOBS_API_VER >= MAKE_SEMANTIC_VERSION(31, 1, 0)
|
|
if (!weakCanvas) {
|
|
return {};
|
|
}
|
|
#endif
|
|
|
|
OBSCanvasAutoRelease canvas = obs_weak_canvas_get_canvas(weakCanvas);
|
|
static const auto enumCanvasScenes = [](void *listPtr,
|
|
obs_source_t *source) -> bool {
|
|
auto list = static_cast<QStringList *>(listPtr);
|
|
*list << obs_source_get_name(source);
|
|
return true;
|
|
};
|
|
|
|
QStringList list;
|
|
obs_canvas_enum_scenes(canvas, enumCanvasScenes, &list);
|
|
list.sort();
|
|
return list;
|
|
}
|
|
|
|
void SceneSelectionWidget::Reset()
|
|
{
|
|
auto previousSel = _currentSelection;
|
|
PopulateSceneSelection(_currentSelection._canvas);
|
|
SetScene(previousSel);
|
|
}
|
|
|
|
void SceneSelectionWidget::PopulateSceneSelection(obs_weak_canvas_t *canvas)
|
|
{
|
|
if (_forceMainCanvas) {
|
|
canvas = getWeakRefToMainCanvas();
|
|
}
|
|
|
|
_scenes->clear();
|
|
if ((_current || _previous)) {
|
|
const bool isMain = IsMainCanvas(canvas);
|
|
const QStringList order = getOrderList(
|
|
_current, _previous && isMain, _preview && isMain);
|
|
AddSelectionGroup(_scenes, order);
|
|
}
|
|
_placeholderEndIdx = _scenes->count();
|
|
|
|
if (_variables) {
|
|
const QStringList variables = GetVariablesNameList();
|
|
AddSelectionGroup(_scenes, variables);
|
|
}
|
|
_variablesEndIdx = _scenes->count();
|
|
|
|
if (_sceneGroups) {
|
|
const QStringList sceneGroups = getSceneGroupsList();
|
|
AddSelectionGroup(_scenes, sceneGroups);
|
|
}
|
|
_groupsEndIdx = _scenes->count();
|
|
|
|
const QStringList scenes = getCanvasScenesList(canvas);
|
|
AddSelectionGroup(_scenes, scenes);
|
|
_scenesEndIdx = _scenes->count();
|
|
|
|
// Remove last separator
|
|
_scenes->removeItem(_scenes->count() - 1);
|
|
_scenes->setCurrentIndex(-1);
|
|
|
|
_isPopulated = true;
|
|
|
|
Resize();
|
|
}
|
|
|
|
SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool variables,
|
|
bool sceneGroups, bool previous,
|
|
bool current, bool preview)
|
|
: QWidget(parent),
|
|
_scenes(new FilterComboBox(
|
|
this, obs_module_text("AdvSceneSwitcher.selectScene"))),
|
|
_canvas(new CanvasSelection(this)),
|
|
_current(current),
|
|
_previous(previous),
|
|
_preview(preview),
|
|
_variables(variables),
|
|
_sceneGroups(sceneGroups)
|
|
{
|
|
_scenes->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
|
_scenes->setDuplicatesEnabled(true);
|
|
|
|
QWidget::connect(_scenes, SIGNAL(currentIndexChanged(int)), this,
|
|
SLOT(SelectionChanged(int)));
|
|
QWidget::connect(_canvas, SIGNAL(CanvasChanged(const OBSWeakCanvas &)),
|
|
this, SLOT(CanvasChangedSlot(const OBSWeakCanvas &)));
|
|
QWidget::connect(_canvas, SIGNAL(CanvasChanged(const OBSWeakCanvas &)),
|
|
this, SIGNAL(CanvasChanged(const OBSWeakCanvas &)));
|
|
|
|
// Variables
|
|
QWidget::connect(VariableSignalManager::Instance(),
|
|
SIGNAL(Add(const QString &)), this,
|
|
SLOT(ItemAdd(const QString &)));
|
|
QWidget::connect(VariableSignalManager::Instance(),
|
|
SIGNAL(Remove(const QString &)), this,
|
|
SLOT(ItemRemove(const QString &)));
|
|
QWidget::connect(VariableSignalManager::Instance(),
|
|
SIGNAL(Rename(const QString &, const QString &)), this,
|
|
SLOT(ItemRename(const QString &, const QString &)));
|
|
|
|
auto layout = new QHBoxLayout();
|
|
layout->addWidget(_canvas);
|
|
layout->addWidget(_scenes);
|
|
setLayout(layout);
|
|
setContentsMargins(0, 0, 0, 0);
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
if (GetCanvasCount() <= 1) {
|
|
LockToMainCanvas();
|
|
}
|
|
|
|
Resize();
|
|
}
|
|
|
|
void SceneSelectionWidget::SetScene(const SceneSelection &s)
|
|
{
|
|
int idx = -1;
|
|
|
|
_canvas->SetCanvas(s._canvas);
|
|
if (!_isPopulated) {
|
|
PopulateSceneSelection(s._canvas);
|
|
}
|
|
|
|
switch (s.GetType()) {
|
|
case SceneSelection::Type::SCENE: {
|
|
if (_scenesEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
idx = FindIdxInRagne(_scenes, _groupsEndIdx, _scenesEndIdx,
|
|
s.ToString());
|
|
break;
|
|
}
|
|
case SceneSelection::Type::GROUP: {
|
|
if (_groupsEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
idx = FindIdxInRagne(_scenes, _variablesEndIdx, _groupsEndIdx,
|
|
s.ToString());
|
|
break;
|
|
}
|
|
case SceneSelection::Type::PREVIOUS: {
|
|
if (_placeholderEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
|
|
idx = FindIdxInRagne(
|
|
_scenes, _selectIdx, _placeholderEndIdx,
|
|
obs_module_text(
|
|
"AdvSceneSwitcher.selectPreviousScene"));
|
|
break;
|
|
}
|
|
case SceneSelection::Type::CURRENT: {
|
|
if (_placeholderEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
|
|
idx = FindIdxInRagne(
|
|
_scenes, _selectIdx, _placeholderEndIdx,
|
|
obs_module_text("AdvSceneSwitcher.selectCurrentScene"));
|
|
break;
|
|
}
|
|
case SceneSelection::Type::PREVIEW: {
|
|
if (_placeholderEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
|
|
idx = FindIdxInRagne(
|
|
_scenes, _selectIdx, _placeholderEndIdx,
|
|
obs_module_text("AdvSceneSwitcher.selectPreviewScene"));
|
|
break;
|
|
}
|
|
case SceneSelection::Type::VARIABLE: {
|
|
if (_variablesEndIdx == -1) {
|
|
idx = -1;
|
|
break;
|
|
}
|
|
idx = FindIdxInRagne(_scenes, _placeholderEndIdx,
|
|
_variablesEndIdx, s.ToString());
|
|
break;
|
|
default:
|
|
idx = -1;
|
|
break;
|
|
}
|
|
}
|
|
_scenes->setCurrentIndex(idx);
|
|
_currentSelection = s;
|
|
|
|
Resize();
|
|
}
|
|
|
|
void SceneSelectionWidget::LockToMainCanvas()
|
|
{
|
|
_forceMainCanvas = true;
|
|
_canvas->hide();
|
|
}
|
|
|
|
void SceneSelectionWidget::showEvent(QShowEvent *event)
|
|
{
|
|
QWidget::showEvent(event);
|
|
const QSignalBlocker b1(_scenes);
|
|
const QSignalBlocker b2(_canvas);
|
|
Reset();
|
|
}
|
|
|
|
bool SceneSelectionWidget::IsCurrentSceneSelected(const QString &name)
|
|
{
|
|
return name == QString::fromStdString((obs_module_text(
|
|
"AdvSceneSwitcher.selectCurrentScene")));
|
|
}
|
|
|
|
bool SceneSelectionWidget::IsPreviousSceneSelected(const QString &name)
|
|
{
|
|
return name == QString::fromStdString((obs_module_text(
|
|
"AdvSceneSwitcher.selectPreviousScene")));
|
|
}
|
|
|
|
bool SceneSelectionWidget::IsPreviewSceneSelected(const QString &name)
|
|
{
|
|
return name == QString::fromStdString((obs_module_text(
|
|
"AdvSceneSwitcher.selectPreviewScene")));
|
|
}
|
|
|
|
void SceneSelectionWidget::SelectionChanged(int)
|
|
{
|
|
_currentSelection = CurrentSelection();
|
|
emit SceneChanged(_currentSelection);
|
|
}
|
|
|
|
void SceneSelectionWidget::CanvasChangedSlot(const OBSWeakCanvas &)
|
|
{
|
|
_currentSelection = CurrentSelection();
|
|
Reset();
|
|
}
|
|
|
|
void SceneSelectionWidget::ItemAdd(const QString &)
|
|
{
|
|
const QSignalBlocker b1(_scenes);
|
|
const QSignalBlocker b2(_canvas);
|
|
Reset();
|
|
}
|
|
|
|
bool SceneSelectionWidget::NameUsed(const QString &name)
|
|
{
|
|
if (_currentSelection._type == SceneSelection::Type::GROUP &&
|
|
_currentSelection._group &&
|
|
_currentSelection._group->name == name.toStdString()) {
|
|
return true;
|
|
}
|
|
return _currentSelection._type == SceneSelection::Type::VARIABLE &&
|
|
_scenes->currentText() == name;
|
|
}
|
|
|
|
void SceneSelectionWidget::ItemRemove(const QString &name)
|
|
{
|
|
if (!NameUsed(name)) {
|
|
blockSignals(true);
|
|
}
|
|
Reset();
|
|
blockSignals(false);
|
|
}
|
|
|
|
void SceneSelectionWidget::ItemRename(const QString &, const QString &)
|
|
{
|
|
const QSignalBlocker b1(_scenes);
|
|
const QSignalBlocker b2(_canvas);
|
|
Reset();
|
|
}
|
|
|
|
void SceneSelectionWidget::Resize()
|
|
{
|
|
_scenes->adjustSize();
|
|
_scenes->updateGeometry();
|
|
_canvas->adjustSize();
|
|
_canvas->updateGeometry();
|
|
adjustSize();
|
|
updateGeometry();
|
|
}
|
|
|
|
} // namespace advss
|