diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a6fb47e..e4e4abae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,8 @@ target_sources( src/utils/section.hpp src/utils/slider-spinbox.cpp src/utils/slider-spinbox.hpp + src/utils/source-selection.cpp + src/utils/source-selection.hpp src/utils/transition-selection.cpp src/utils/transition-selection.hpp src/utils/utility.cpp diff --git a/src/utils/source-selection.cpp b/src/utils/source-selection.cpp new file mode 100644 index 00000000..c3658a2a --- /dev/null +++ b/src/utils/source-selection.cpp @@ -0,0 +1,246 @@ +#include "source-selection.hpp" +#include "advanced-scene-switcher.hpp" + +constexpr std::string_view typeSaveName = "type"; +constexpr std::string_view nameSaveName = "name"; + +void SourceSelection::Save(obs_data_t *obj, const char *name) const +{ + auto data = obs_data_create(); + obs_data_set_int(data, typeSaveName.data(), static_cast(_type)); + switch (_type) { + case Type::SOURCE: + obs_data_set_string(data, nameSaveName.data(), + GetWeakSourceName(_source).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_obj(obj, name, data); + obs_data_release(data); +} + +void SourceSelection::Load(obs_data_t *obj, const char *name) +{ + auto data = obs_data_get_obj(obj, name); + _type = static_cast(obs_data_get_int(data, typeSaveName.data())); + auto targetName = obs_data_get_string(data, nameSaveName.data()); + switch (_type) { + case Type::SOURCE: + _source = GetWeakSourceByName(targetName); + break; + case Type::VARIABLE: + _variable = GetWeakVariableByName(targetName); + break; + default: + break; + } + if (!obs_data_has_user_value(data, typeSaveName.data())) { + LoadFallback(obj, name); + } + obs_data_release(data); +} + +void SourceSelection::LoadFallback(obs_data_t *obj, const char *name) +{ + blog(LOG_INFO, "Falling back to Load() without variable support"); + _type = Type::SOURCE; + auto targetName = obs_data_get_string(obj, name); + _source = GetWeakSourceByName(targetName); +} + +OBSWeakSource SourceSelection::GetSource() const +{ + switch (_type) { + case Type::SOURCE: + return _source; + case Type::VARIABLE: { + auto var = _variable.lock(); + if (!var) { + return nullptr; + } + return GetWeakSourceByName(var->Value().c_str()); + } + default: + break; + } + return nullptr; +} + +void SourceSelection::SetSource(OBSWeakSource source) +{ + _type = Type::SOURCE; + _source = source; +} + +std::string SourceSelection::ToString() const +{ + switch (_type) { + case Type::SOURCE: + return GetWeakSourceName(_source); + case Type::VARIABLE: { + auto var = _variable.lock(); + if (!var) { + return ""; + } + return var->Name(); + } + default: + break; + } + return ""; +} + +SourceSelection SourceSelectionWidget::CurrentSelection() +{ + SourceSelection s; + const int idx = currentIndex(); + const auto name = currentText(); + + if (idx < _variablesEndIdx) { + s._type = SourceSelection::Type::VARIABLE; + s._variable = GetWeakVariableByQString(name); + } else if (idx < _sourcesEndIdx) { + s._type = SourceSelection::Type::SOURCE; + s._source = GetWeakSourceByQString(name); + } + return s; +} + +void SourceSelectionWidget::Reset() +{ + auto previousSel = _currentSelection; + PopulateSelection(); + SetSource(previousSel); +} + +void SourceSelectionWidget::PopulateSelection() +{ + clear(); + addSelectionEntry(this, + obs_module_text("AdvSceneSwitcher.selectSource")); + insertSeparator(count()); + + if (_addVariables) { + const QStringList variables = GetVariablesNameList(); + addSelectionGroup(this, variables); + } + _variablesEndIdx = count(); + + addSelectionGroup(this, _sourceNames); + _sourcesEndIdx = count(); + + // Remove last separator + removeItem(count() - 1); + setCurrentIndex(0); +} + +SourceSelectionWidget::SourceSelectionWidget(QWidget *parent, + const QStringList &sourceNames, + bool addVariables) + : QComboBox(parent), + _addVariables(addVariables), + _sourceNames(sourceNames) +{ + setDuplicatesEnabled(true); + PopulateSelection(); + + QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SelectionChanged(const QString &))); + + // Variables + QWidget::connect(window(), SIGNAL(VariableAdded(const QString &)), this, + SLOT(ItemAdd(const QString &))); + QWidget::connect(window(), SIGNAL(VariableRemoved(const QString &)), + this, SLOT(ItemRemove(const QString &))); + QWidget::connect( + window(), + SIGNAL(VariableRenamed(const QString &, const QString &)), this, + SLOT(ItemRename(const QString &, const QString &))); +} + +void SourceSelectionWidget::SetSource(const SourceSelection &s) +{ + int idx = 0; + + switch (s.GetType()) { + case SourceSelection::Type::SOURCE: { + if (_sourcesEndIdx == -1) { + idx = 0; + break; + } + idx = findIdxInRagne(this, _variablesEndIdx, _sourcesEndIdx, + s.ToString()); + break; + } + case SourceSelection::Type::VARIABLE: { + if (_variablesEndIdx == -1) { + idx = 0; + break; + } + idx = findIdxInRagne(this, _selectIdx, _variablesEndIdx, + s.ToString()); + break; + default: + idx = 0; + break; + } + } + setCurrentIndex(idx); + _currentSelection = s; +} + +void SourceSelectionWidget::SetSourceNameList(const QStringList &list) +{ + _sourceNames = list; + Reset(); +} + +void SourceSelectionWidget::SelectionChanged(const QString &) +{ + _currentSelection = CurrentSelection(); + emit SourceChanged(_currentSelection); +} + +void SourceSelectionWidget::ItemAdd(const QString &) +{ + blockSignals(true); + Reset(); + blockSignals(false); +} + +bool SourceSelectionWidget::NameUsed(const QString &name) +{ + if (_currentSelection._type == SourceSelection::Type::VARIABLE) { + auto var = _currentSelection._variable.lock(); + if (var && var->Name() == name.toStdString()) { + return true; + } + } + return false; +} + +void SourceSelectionWidget::ItemRemove(const QString &name) +{ + if (!NameUsed(name)) { + blockSignals(true); + } + Reset(); + blockSignals(false); +} + +void SourceSelectionWidget::ItemRename(const QString &, const QString &) +{ + blockSignals(true); + Reset(); + blockSignals(false); +} diff --git a/src/utils/source-selection.hpp b/src/utils/source-selection.hpp new file mode 100644 index 00000000..b2d5211a --- /dev/null +++ b/src/utils/source-selection.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "variable.hpp" +#include "utility.hpp" + +#include + + +class SourceSelection { +public: + void Save(obs_data_t *obj, const char *name = "source") const; + void Load(obs_data_t *obj, const char *name = "source"); + + enum class Type { + SOURCE, + VARIABLE, + }; + + Type GetType() const { return _type; } + OBSWeakSource GetSource() const; + void SetSource(OBSWeakSource); + std::string ToString() const; + +private: + // TODO: Remove in future version + // Used for backwards compatability to older settings versions + void LoadFallback(obs_data_t *obj, const char *name); + + OBSWeakSource _source; + std::weak_ptr _variable; + Type _type = Type::SOURCE; + friend class SourceSelectionWidget; +}; + +class SourceSelectionWidget : public QComboBox { + Q_OBJECT + +public: + SourceSelectionWidget(QWidget *parent, const QStringList &sourceNames, + bool addVariables = true); + void SetSource(const SourceSelection &); + void SetSourceNameList(const QStringList &); +signals: + void SourceChanged(const SourceSelection &); + +private slots: + void SelectionChanged(const QString &name); + void ItemAdd(const QString &name); + void ItemRemove(const QString &name); + void ItemRename(const QString &oldName, const QString &newName); + +private: + void Reset(); + SourceSelection CurrentSelection(); + void PopulateSelection(); + bool NameUsed(const QString &name); + + bool _addVariables; + QStringList _sourceNames; + SourceSelection _currentSelection; + + // Order of entries + // 1. "select entry" entry + // 2. Variables + // 3. Regular sources + const int _selectIdx = 0; + int _variablesEndIdx = -1; + int _sourcesEndIdx = -1; +};