#include "scene-item-selection.hpp" #include "layout-helpers.hpp" #include "obs-module-helper.hpp" #include "selection-helpers.hpp" #include "source-helpers.hpp" #include "ui-helpers.hpp" #include namespace advss { constexpr std::string_view typeSaveName = "type"; constexpr std::string_view itemSaveName = "item"; constexpr std::string_view indexSaveName = "index"; constexpr std::string_view indexEndSaveName = "indexEnd"; constexpr std::string_view nameConflictIndexSaveName = "idx"; constexpr std::string_view nameConflictIndexSelectionSaveName = "idxType"; constexpr std::string_view patternSaveName = "pattern"; const static std::map types = { {SceneItemSelection::Type::SOURCE_NAME, "AdvSceneSwitcher.sceneItemSelection.type.sourceName"}, {SceneItemSelection::Type::VARIABLE_NAME, "AdvSceneSwitcher.sceneItemSelection.type.sourceVariable"}, {SceneItemSelection::Type::SOURCE_NAME_PATTERN, "AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern"}, {SceneItemSelection::Type::SOURCE_GROUP, "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup"}, {SceneItemSelection::Type::SOURCE_TYPE, "AdvSceneSwitcher.sceneItemSelection.type.sourceType"}, {SceneItemSelection::Type::INDEX, "AdvSceneSwitcher.sceneItemSelection.type.index"}, {SceneItemSelection::Type::INDEX_RANGE, "AdvSceneSwitcher.sceneItemSelection.type.indexRange"}, {SceneItemSelection::Type::ALL, "AdvSceneSwitcher.sceneItemSelection.type.all"}, }; /* ------------------------------------------------------------------------- */ struct NameMatchData { std::string name; std::vector items = {}; }; static bool getSceneItemsByNameHelper(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto itemInfo = reinterpret_cast(ptr); auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item)); if (itemInfo->name == sourceName) { itemInfo->items.emplace_back(item); } if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, getSceneItemsByNameHelper, ptr); } return true; } static std::vector getSceneItemsWithName(obs_scene_t *scene, const std::string &name) { NameMatchData itemInfo = {name}; obs_scene_enum_items(scene, getSceneItemsByNameHelper, &itemInfo); return itemInfo.items; } struct NamePatternMatchData { std::string pattern; const RegexConfig ®ex; std::vector items = {}; }; static bool getSceneItemsByPatternHelper(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto data = reinterpret_cast(ptr); auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item)); if (data->regex.Matches(sourceName, data->pattern)) { data->items.emplace_back(item); } if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, getSceneItemsByPatternHelper, ptr); } return true; } struct ItemCountData { std::string name; int count = 0; }; static bool countSceneItemName(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto data = reinterpret_cast(ptr); if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, countSceneItemName, ptr); } auto name = obs_source_get_name(obs_sceneitem_get_source(item)); if (name == data->name) { data->count++; } return true; } static int getCountOfSceneItemOccurance(const SceneSelection &s, const std::string &name, bool enumAllScenes = true) { ItemCountData data{name}; if (enumAllScenes && (s.GetType() != SceneSelection::Type::SCENE)) { auto enumScenes = [](void *param, obs_source_t *source) { if (!source) { return true; } auto data = reinterpret_cast(param); auto scene = obs_scene_from_source(source); obs_scene_enum_items(scene, countSceneItemName, data); return true; }; obs_enum_scenes(enumScenes, &data); } else { auto source = obs_weak_source_get_source(s.GetScene(false)); auto scene = obs_scene_from_source(source); obs_scene_enum_items(scene, countSceneItemName, &data); obs_source_release(source); } return data.count; } struct IdxData { int idxStart = 0; int idxEnd = -1; int curIdx = 0; std::vector items = {}; }; static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto data = reinterpret_cast(ptr); if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, getSceneItemAtIdx, ptr); } if (data->curIdx > data->idxEnd) { return false; } if (data->curIdx >= data->idxStart && data->curIdx <= data->idxEnd) { data->items.emplace_back(item); } data->curIdx++; return true; } struct GroupData { std::string type; std::vector items = {}; }; static bool getSceneItemsOfType(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto data = reinterpret_cast(ptr); auto sourceTypeName = obs_source_get_display_name( obs_source_get_id(obs_sceneitem_get_source(item))); if (sourceTypeName && data->type == sourceTypeName) { data->items.emplace_back(item); } if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, getSceneItemsOfType, ptr); } return true; } static bool getAllSceneItems(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto items = reinterpret_cast *>(ptr); items->emplace_back(item); if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, getAllSceneItems, ptr); } return true; } /* ------------------------------------------------------------------------- */ void SceneItemSelection::Save(obs_data_t *obj, const char *name) const { auto data = obs_data_create(); obs_data_set_int(data, typeSaveName.data(), static_cast(_type)); obs_data_set_int(data, nameConflictIndexSelectionSaveName.data(), static_cast(_nameConflictSelectionType)); if (_nameConflictSelectionType == NameConflictSelection::INDIVIDUAL) { obs_data_set_int(data, nameConflictIndexSaveName.data(), _nameConflictSelectionIndex); } else { obs_data_set_int(data, nameConflictIndexSaveName.data(), 0); } switch (_type) { case SceneItemSelection::Type::SOURCE_NAME: obs_data_set_string(data, itemSaveName.data(), GetWeakSourceName(_source).c_str()); break; case SceneItemSelection::Type::VARIABLE_NAME: { auto var = _variable.lock(); if (var) { obs_data_set_string(data, itemSaveName.data(), var->Name().c_str()); } break; } case SceneItemSelection::Type::SOURCE_NAME_PATTERN: _pattern.Save(data, patternSaveName.data()); _regex.Save(data); break; case SceneItemSelection::Type::SOURCE_GROUP: obs_data_set_string(data, itemSaveName.data(), GetWeakSourceName(_source).c_str()); break; case SceneItemSelection::Type::SOURCE_TYPE: obs_data_set_string(obj, "sourceGroup", _sourceType.c_str()); break; case SceneItemSelection::Type::INDEX: _index.Save(data, indexSaveName.data()); break; case SceneItemSelection::Type::INDEX_RANGE: _index.Save(data, indexSaveName.data()); _indexEnd.Save(data, indexEndSaveName.data()); break; default: break; } obs_data_set_obj(obj, name, data); obs_data_release(data); } // TODO: Remove in future version void SceneItemSelection::Load(obs_data_t *obj, const char *name, const char *typeName, const char *idxName) { _type = Type::SOURCE_NAME; _nameConflictSelectionType = static_cast( obs_data_get_int(obj, typeName)); _nameConflictSelectionIndex = obs_data_get_int(obj, idxName); auto sceneItemName = obs_data_get_string(obj, name); _source = GetWeakSourceByName(sceneItemName); } void SceneItemSelection::Load(obs_data_t *obj, const char *name) { // TODO: Remove in future version if (!obs_data_has_user_value(obj, name)) { Load(obj, "sceneItem", "sceneItemTarget", "sceneItemIdx"); return; } auto data = obs_data_get_obj(obj, name); _type = static_cast(obs_data_get_int(data, typeSaveName.data())); _nameConflictSelectionType = static_cast( obs_data_get_int(data, nameConflictIndexSelectionSaveName.data())); _nameConflictSelectionIndex = obs_data_get_int(data, nameConflictIndexSaveName.data()); const auto itemName = obs_data_get_string(data, itemSaveName.data()); switch (_type) { case SceneItemSelection::Type::SOURCE_NAME: _source = GetWeakSourceByName(itemName); break; case SceneItemSelection::Type::VARIABLE_NAME: _variable = GetWeakVariableByName(itemName); break; case SceneItemSelection::Type::SOURCE_NAME_PATTERN: _pattern.Load(data, patternSaveName.data()); _regex.Load(data); _regex.SetEnabled(true); // No reason to disable it break; case SceneItemSelection::Type::SOURCE_GROUP: _source = GetWeakSourceByName(itemName); break; case SceneItemSelection::Type::SOURCE_TYPE: _sourceType = obs_data_get_string(obj, "sourceGroup"); break; case SceneItemSelection::Type::INDEX: _index.Load(data, indexSaveName.data()); break; case SceneItemSelection::Type::INDEX_RANGE: _index.Load(data, indexSaveName.data()); _indexEnd.Load(data, indexEndSaveName.data()); break; default: break; } obs_data_release(data); } void SceneItemSelection::SetSourceTypeSelection(const char *type) { _type = Type::SOURCE_TYPE; _sourceType = type; } void SceneItemSelection::ResolveVariables() { _index.ResolveVariables(); _indexEnd.ResolveVariables(); _pattern = std::string(_pattern); if (_type != Type::VARIABLE_NAME) { return; } _type = Type::SOURCE_NAME; auto variable = _variable.lock(); if (variable) { _source = GetWeakSourceByName(variable->Value().c_str()); } else { _source = nullptr; } } void SceneItemSelection::ReduceBadedOnIndexSelection( std::vector &items) const { if (_nameConflictSelectionType == SceneItemSelection::NameConflictSelection::ALL || _nameConflictSelectionType == SceneItemSelection::NameConflictSelection::ANY) { return; } // Index order starts at the bottom and increases to the top // As this might be confusing reverse that order internally int idx = items.size() - 1 - _nameConflictSelectionIndex; if (idx < 0 || idx >= (int)items.size()) { items.clear(); return; } items = {items[idx]}; } std::vector SceneItemSelection::GetSceneItemsByName( const SceneSelection &sceneSelection) const { auto s = obs_weak_source_get_source(sceneSelection.GetScene(false)); OBSSceneAutoRelease scene = obs_scene_from_source(s); std::string name; if (_type == Type::VARIABLE_NAME) { auto var = _variable.lock(); if (!var) { return {}; } name = var->Value(); } else { name = GetWeakSourceName(_source); } auto items = getSceneItemsWithName(scene, name); ReduceBadedOnIndexSelection(items); return items; } std::vector SceneItemSelection::GetSceneItemsByPattern( const SceneSelection &sceneSelection) const { auto s = obs_weak_source_get_source(sceneSelection.GetScene(false)); auto scene = obs_scene_from_source(s); NamePatternMatchData data{_pattern, _regex}; obs_scene_enum_items(scene, getSceneItemsByPatternHelper, &data); obs_source_release(s); ReduceBadedOnIndexSelection(data.items); return data.items; } std::vector SceneItemSelection::GetSceneItemsOfGroup() const { OBSSourceAutoRelease group = OBSGetStrongRef(_source); obs_scene_t *groupScene = obs_group_from_source(group); std::vector sceneItems; obs_scene_enum_items(groupScene, getAllSceneItems, &sceneItems); return sceneItems; } std::vector SceneItemSelection::GetSceneItemsByType( const SceneSelection &sceneSelection) const { if (_sourceType.empty()) { return {}; } auto s = obs_weak_source_get_source(sceneSelection.GetScene(false)); auto scene = obs_scene_from_source(s); GroupData data{_sourceType}; obs_scene_enum_items(scene, getSceneItemsOfType, &data); obs_source_release(s); ReduceBadedOnIndexSelection(data.items); return data.items; } std::vector SceneItemSelection::GetSceneItemsByIdx( const SceneSelection &sceneSelection) const { if (!_index.HasValidValue()) { return {}; } auto sceneWeakSource = sceneSelection.GetScene(false); int count = GetSceneItemCount(sceneWeakSource); if (count == 0) { return {}; } // Index order starts at the bottom and increases to the top // As this might be confusing reverse that order internally // // Also subtract 1 as the user facing index starts at 1 int idx = count - _index.GetValue(); int idxEnd = _type == Type::INDEX_RANGE ? count - _indexEnd.GetValue() : idx; if (idx > idxEnd) { std::swap(idx, idxEnd); } IdxData data{idx, idxEnd}; auto s = obs_weak_source_get_source(sceneWeakSource); auto scene = obs_scene_from_source(s); obs_scene_enum_items(scene, getSceneItemAtIdx, &data); obs_source_release(s); return data.items; } std::vector SceneItemSelection::GetAllSceneItems(const SceneSelection &sceneSelection) const { auto s = obs_weak_source_get_source(sceneSelection.GetScene(false)); auto scene = obs_scene_from_source(s); std::vector items; obs_scene_enum_items(scene, getAllSceneItems, &items); obs_source_release(s); return items; } SceneItemSelection::NameConflictSelection SceneItemSelection::GetIndexType() const { return _nameConflictSelectionType; } std::vector SceneItemSelection::GetSceneItems(const SceneSelection &sceneSelection) const { switch (_type) { case Type::SOURCE_NAME: case Type::VARIABLE_NAME: return GetSceneItemsByName(sceneSelection); case Type::SOURCE_NAME_PATTERN: return GetSceneItemsByPattern(sceneSelection); case Type::SOURCE_GROUP: return GetSceneItemsOfGroup(); case Type::SOURCE_TYPE: return GetSceneItemsByType(sceneSelection); case Type::INDEX: case Type::INDEX_RANGE: return GetSceneItemsByIdx(sceneSelection); case Type::ALL: return GetAllSceneItems(sceneSelection); default: break; } return {}; } std::string SceneItemSelection::ToString(bool resolve) const { switch (_type) { case Type::SOURCE_NAME: return GetWeakSourceName(_source); case Type::VARIABLE_NAME: { auto var = _variable.lock(); if (!var) { return ""; } if (resolve) { return var->Name() + "[" + var->Value() + "]"; } return var->Name(); } case Type::SOURCE_NAME_PATTERN: if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern")) + " \"" + std::string(_pattern) + "\""; } return ""; case Type::SOURCE_GROUP: if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup")) + " \"" + std::string(_sourceType) + "\""; } return _sourceType; case Type::SOURCE_TYPE: if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceType")) + " \"" + std::string(_sourceType) + "\""; } return _sourceType; case Type::INDEX: if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.index")) + " " + std::to_string(_index); } return ""; case Type::INDEX_RANGE: if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.indexRange")) + " " + std::to_string(_index) + " - " + std::to_string(_indexEnd); } return ""; case Type::ALL: return obs_module_text( "AdvSceneSwitcher.sceneItemSelection.all"); default: break; } return ""; } static bool populateSceneItemSelectionHelper(obs_scene_t *, obs_sceneitem_t *item, void *ptr) { auto names = reinterpret_cast(ptr); if (obs_sceneitem_is_group(item)) { obs_scene_t *scene = obs_sceneitem_group_get_scene(item); obs_scene_enum_items(scene, populateSceneItemSelectionHelper, ptr); } auto name = obs_source_get_name(obs_sceneitem_get_source(item)); names->append(name); return true; } static QStringList getSceneItemsList(SceneSelection &s) { QStringList names; if (s.GetType() != SceneSelection::Type::SCENE) { auto enumScenes = [](void *param, obs_source_t *source) { if (!source) { return true; } auto names = reinterpret_cast(param); auto scene = obs_scene_from_source(source); obs_scene_enum_items( scene, populateSceneItemSelectionHelper, names); return true; }; obs_enum_scenes(enumScenes, &names); } else { auto source = obs_weak_source_get_source(s.GetScene(false)); auto scene = obs_scene_from_source(source); obs_scene_enum_items(scene, populateSceneItemSelectionHelper, &names); obs_source_release(source); } names.removeDuplicates(); names.sort(); return names; } void SceneItemSelectionWidget::PopulateItemSelection() { _sources->clear(); const QStringList sceneItems = getSceneItemsList(_scene); AddSelectionGroup(_sources, sceneItems, false); _sources->setCurrentIndex(-1); } static inline void populateMessageTypeSelection(QComboBox *list) { for (const auto &[type, name] : types) { list->addItem(obs_module_text(name.c_str()), static_cast(type)); } } static void populateTypeList(std::set &list, std::function enumFunc) { size_t idx = 0; char buffer[512] = {}; const char **idPtr = (const char **)(&buffer); while (enumFunc(idx++, idPtr)) { // The audio_line source type is only used OBS internally if (strcmp(*idPtr, "audio_line") == 0) { continue; } QString name = obs_source_get_display_name(*idPtr); if (name.isEmpty()) { list.insert(*idPtr); } else { list.insert(name); } } } static void populateSourceTypeSelection(QComboBox *list) { std::set sourceTypes; populateTypeList(sourceTypes, obs_enum_source_types); std::set filterTypes; populateTypeList(filterTypes, obs_enum_filter_types); std::set transitionTypes; populateTypeList(transitionTypes, obs_enum_transition_types); for (const auto &name : sourceTypes) { if (name.isEmpty()) { continue; } if (filterTypes.find(name) == filterTypes.end() && transitionTypes.find(name) == transitionTypes.end()) { list->addItem(name); } } list->model()->sort(0); AddSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem")); list->setCurrentIndex(0); } static void populateSourceGroupSelection(QComboBox *list) { auto sourceEnum = [](void *param, obs_source_t *source) -> bool /* -- */ { QStringList *list = reinterpret_cast(param); if (obs_source_is_group(source)) { *list << obs_source_get_name(source); } return true; }; QStringList names; obs_enum_sources(sourceEnum, &names); for (const auto &name : names) { if (name.isEmpty()) { continue; } list->addItem(name); } list->model()->sort(0); AddSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem")); list->setCurrentIndex(0); } SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent, bool showAll, Placeholder type) : QWidget(parent), _controlsLayout(new QHBoxLayout), _sources(new FilterComboBox( this, obs_module_text("AdvSceneSwitcher.selectItem"))), _sourceGroups(new FilterComboBox( this, obs_module_text("AdvSceneSwitcher.selectItem"))), _variables(new VariableSelection(this)), _nameConflictIndex(new QComboBox(this)), _index(new VariableSpinBox(this)), _indexEnd(new VariableSpinBox(this)), _sourceTypes(new QComboBox(this)), _pattern(new VariableLineEdit(this)), _regex(new RegexConfigWidget(this, false)), _changeType(new QPushButton(this)), _hasPlaceholderEntry(showAll), _placeholder(type) { _sources->setSizeAdjustPolicy(QComboBox::AdjustToContents); _sourceGroups->setSizeAdjustPolicy(QComboBox::AdjustToContents); _nameConflictIndex->setSizeAdjustPolicy(QComboBox::AdjustToContents); _changeType->setMaximumWidth(22); SetButtonIcon(_changeType, GetThemeTypeName() == "Light" ? ":/settings/images/settings/general.svg" : "theme:Dark/settings/general.svg"); _changeType->setFlat(true); _changeType->setToolTip(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.configure")); _index->setMinimum(1); _index->setMaximum(999); _index->setSuffix("."); _indexEnd->setMinimum(1); _indexEnd->setMaximum(999); _indexEnd->setSuffix("."); populateSourceGroupSelection(_sourceGroups); populateSourceTypeSelection(_sourceTypes); QWidget::connect(_sources, SIGNAL(currentIndexChanged(int)), this, SLOT(SourceChanged(int))); QWidget::connect(_sourceGroups, SIGNAL(currentIndexChanged(int)), this, SLOT(SourceGroupChanged(int))); QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), this, SLOT(VariableChanged(const QString &))); QWidget::connect(_nameConflictIndex, SIGNAL(currentIndexChanged(int)), this, SLOT(NameConflictIndexChanged(int))); QWidget::connect(_sourceTypes, SIGNAL(currentTextChanged(const QString &)), this, SLOT(SourceTypeChanged(const QString &))); QWidget::connect( _index, SIGNAL(NumberVariableChanged(const NumberVariable &)), this, SLOT(IndexChanged(const NumberVariable &))); QWidget::connect( _indexEnd, SIGNAL(NumberVariableChanged(const NumberVariable &)), this, SLOT(IndexEndChanged(const NumberVariable &))); QWidget::connect(_pattern, SIGNAL(editingFinished()), this, SLOT(PatternChanged())); QWidget::connect(_regex, SIGNAL(RegexConfigChanged(const RegexConfig &)), this, SLOT(RegexChanged(const RegexConfig &))); QWidget::connect(_changeType, SIGNAL(clicked()), this, SLOT(ChangeType())); _controlsLayout->setContentsMargins(0, 0, 0, 0); auto layout = new QHBoxLayout; layout->setContentsMargins(0, 0, 0, 0); layout->addLayout(_controlsLayout); layout->addWidget(_changeType); setLayout(layout); _nameConflictIndex->hide(); } void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item) { int idx = item._nameConflictSelectionIndex; if (_hasPlaceholderEntry) { idx += 1; } _nameConflictIndex->setCurrentIndex(idx); _sources->setCurrentIndex(_sources->findText( QString::fromStdString(GetWeakSourceName(item._source)))); _sourceGroups->setCurrentText( QString::fromStdString(GetWeakSourceName(item._source))); _variables->SetVariable(item._variable); _index->SetValue(item._index); _indexEnd->SetValue(item._indexEnd); _sourceTypes->setCurrentIndex(_sourceTypes->findText( QString::fromStdString(item._sourceType))); _pattern->setText(item._pattern); _regex->SetRegexConfig(item._regex); _currentSelection = item; { const QSignalBlocker b(_nameConflictIndex); SetNameConflictVisibility(); } switch (item._nameConflictSelectionType) { case SceneItemSelection::NameConflictSelection::ALL: case SceneItemSelection::NameConflictSelection::ANY: _placeholder = Placeholder::ALL; _nameConflictIndex->setCurrentIndex(0); break; case SceneItemSelection::NameConflictSelection::INDIVIDUAL: int idx = item._nameConflictSelectionIndex; if (_hasPlaceholderEntry) { idx += 1; } _nameConflictIndex->setCurrentIndex(idx); break; } SetWidgetVisibility(); } void SceneItemSelectionWidget::SetScene(const SceneSelection &s) { _scene = s; _nameConflictIndex->hide(); if (_currentSelection._type != SceneItemSelection::Type::SOURCE_NAME) { PopulateItemSelection(); return; } auto previous = _currentSelection; PopulateItemSelection(); SetSceneItem(previous); } void SceneItemSelectionWidget::ShowPlaceholder(bool value) { _hasPlaceholderEntry = value; } void SceneItemSelectionWidget::SetPlaceholderType(Placeholder t, bool resetSelection) { _placeholder = t; if (resetSelection) { _sources->setCurrentIndex(-1); } else { auto count = _nameConflictIndex->count() - 1; const QSignalBlocker b(_nameConflictIndex); SetupNameConflictIdxSelection(count > 0 ? count : 1); } } void SceneItemSelectionWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); const QSignalBlocker b1(_sources); const QSignalBlocker b2(this); PopulateItemSelection(); SetSceneItem(_currentSelection); } void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s) { SetScene(s); adjustSize(); } void SceneItemSelectionWidget::SetNameConflictVisibility() { int sceneItemCount = 0; switch (_currentSelection._type) { case SceneItemSelection::Type::SOURCE_NAME: case SceneItemSelection::Type::VARIABLE_NAME: { QString name = ""; if (_currentSelection._type == SceneItemSelection::Type::VARIABLE_NAME) { auto var = _currentSelection._variable.lock(); if (var) { name = QString::fromStdString(var->Value()); } } if (_currentSelection._type == SceneItemSelection::Type::SOURCE_NAME) { name = _sources->currentText(); } if (name.isEmpty()) { break; } sceneItemCount = getCountOfSceneItemOccurance( _scene, name.toStdString()); break; } case SceneItemSelection::Type::SOURCE_GROUP: // There cannot be a name conflict with source groups _nameConflictIndex->hide(); return; case SceneItemSelection::Type::SOURCE_NAME_PATTERN: case SceneItemSelection::Type::SOURCE_TYPE: sceneItemCount = GetSceneItemCount(_scene.GetScene(false)); break; case SceneItemSelection::Type::INDEX: case SceneItemSelection::Type::INDEX_RANGE: case SceneItemSelection::Type::ALL: break; } if (_currentSelection._type == SceneItemSelection::Type::SOURCE_NAME_PATTERN) { int sceneItemCount = GetSceneItemCount(_scene.GetScene(false)); if (sceneItemCount == 0) { _nameConflictIndex->hide(); return; } SetupNameConflictIdxSelection(sceneItemCount); _nameConflictIndex->show(); return; } if (sceneItemCount == 0) { _nameConflictIndex->hide(); return; } if (sceneItemCount > 1) { SetupNameConflictIdxSelection(sceneItemCount); _nameConflictIndex->show(); } else { _nameConflictIndex->hide(); } } void SceneItemSelectionWidget::VariableChanged(const QString &text) { _currentSelection._variable = GetWeakVariableByQString(text); SetNameConflictVisibility(); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::SourceChanged(int) { _currentSelection._source = GetWeakSourceByQString(_sources->currentText()); SetNameConflictVisibility(); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::SourceGroupChanged(int) { _currentSelection._source = GetWeakSourceByQString(_sourceGroups->currentText()); SetNameConflictVisibility(); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::IndexChanged(const IntVariable &index) { _currentSelection._index = index; emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::IndexEndChanged(const IntVariable &index) { _currentSelection._indexEnd = index; emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::NameConflictIndexChanged(int idx) { if (idx < 0) { return; } _currentSelection._nameConflictSelectionIndex = idx; if (_hasPlaceholderEntry && idx == 0) { switch (_placeholder) { case SceneItemSelectionWidget::Placeholder::ALL: _currentSelection._nameConflictSelectionType = SceneItemSelection::NameConflictSelection::ALL; break; case SceneItemSelectionWidget::Placeholder::ANY: _currentSelection._nameConflictSelectionType = SceneItemSelection::NameConflictSelection::ANY; break; } } if (_hasPlaceholderEntry && idx > 0) { _currentSelection._nameConflictSelectionIndex -= 1; _currentSelection._nameConflictSelectionType = SceneItemSelection::NameConflictSelection::INDIVIDUAL; } emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::PatternChanged() { _currentSelection._pattern = _pattern->text().toStdString(); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::RegexChanged(const RegexConfig ®ex) { _currentSelection._regex = regex; // No reason for the regex to be disabled _currentSelection._regex.SetEnabled(true); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::SourceTypeChanged(const QString &text) { if (text == obs_module_text("AdvSceneSwitcher.selectItem")) { _currentSelection._sourceType = ""; } else { _currentSelection._sourceType = text.toStdString(); } emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::ChangeType() { bool accepted = SceneItemTypeSelection::AskForSettings( this, _currentSelection._type); if (!accepted) { return; } SetNameConflictVisibility(); SetWidgetVisibility(); emit SceneItemChanged(_currentSelection); } void SceneItemSelectionWidget::SetupNameConflictIdxSelection(int sceneItemCount) { _nameConflictIndex->clear(); if (_hasPlaceholderEntry) { if (_placeholder == Placeholder::ALL) { _nameConflictIndex->addItem(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.all")); } else { _nameConflictIndex->addItem(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.any")); } } for (int i = 1; i <= sceneItemCount; ++i) { _nameConflictIndex->addItem(QString::number(i) + "."); } adjustSize(); updateGeometry(); } void SceneItemSelectionWidget::ClearWidgets() { _controlsLayout->removeWidget(_nameConflictIndex); _controlsLayout->removeWidget(_sources); _controlsLayout->removeWidget(_sourceGroups); _controlsLayout->removeWidget(_variables); _controlsLayout->removeWidget(_pattern); _controlsLayout->removeWidget(_regex); _controlsLayout->removeWidget(_sourceTypes); _controlsLayout->removeWidget(_index); _controlsLayout->removeWidget(_indexEnd); ClearLayout(_controlsLayout); } void SceneItemSelectionWidget::SetWidgetVisibility() { ClearWidgets(); const std::unordered_map widgetMap = { {"{{nameConflictIndex}}", _nameConflictIndex}, {"{{sourceName}}", _sources}, {"{{sourceGroups}}", _sourceGroups}, {"{{variable}}", _variables}, {"{{pattern}}", _pattern}, {"{{regex}}", _regex}, {"{{sourceTypes}}", _sourceTypes}, {"{{index}}", _index}, {"{{indexEnd}}", _indexEnd}, }; switch (_currentSelection._type) { case SceneItemSelection::Type::SOURCE_NAME: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::VARIABLE_NAME: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_NAME_PATTERN: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_GROUP: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_TYPE: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::INDEX: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.index.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::INDEX_RANGE: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::ALL: PlaceWidgets( obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.all.entry"), _controlsLayout, widgetMap, false); break; default: break; } // Name based _sources->setVisible(_currentSelection._type == SceneItemSelection::Type::SOURCE_NAME); _variables->setVisible(_currentSelection._type == SceneItemSelection::Type::VARIABLE_NAME); const bool isRegexSelection = _currentSelection._type == SceneItemSelection::Type::SOURCE_NAME_PATTERN; _pattern->setVisible(isRegexSelection); _regex->setVisible(isRegexSelection); const bool isNameSelection = _currentSelection._type == SceneItemSelection::Type::VARIABLE_NAME || _currentSelection._type == SceneItemSelection::Type::SOURCE_NAME; // Group based _sourceGroups->setVisible(_currentSelection._type == SceneItemSelection::Type::SOURCE_GROUP); _sourceTypes->setVisible(_currentSelection._type == SceneItemSelection::Type::SOURCE_TYPE); if (!isNameSelection && !isRegexSelection && _currentSelection._type != SceneItemSelection::Type::SOURCE_TYPE && _currentSelection._type != SceneItemSelection::Type::SOURCE_GROUP) { _nameConflictIndex->hide(); } // Index based _index->setVisible(_currentSelection._type == SceneItemSelection::Type::INDEX || _currentSelection._type == SceneItemSelection::Type::INDEX_RANGE); _indexEnd->setVisible(_currentSelection._type == SceneItemSelection::Type::INDEX_RANGE); adjustSize(); updateGeometry(); } SceneItemTypeSelection::SceneItemTypeSelection( QWidget *parent, const SceneItemSelection::Type &type) : QDialog(parent), _typeSelection(new QComboBox(this)), _buttonbox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) { setModal(true); setWindowModality(Qt::WindowModality::WindowModal); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); populateMessageTypeSelection(_typeSelection); _typeSelection->setCurrentIndex( _typeSelection->findData(static_cast(type))); QWidget::connect(_buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept); QWidget::connect(_buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject); auto layout = new QVBoxLayout(); layout->addWidget(_typeSelection); layout->addWidget(_buttonbox, Qt::AlignHCenter); setLayout(layout); } bool SceneItemTypeSelection::AskForSettings(QWidget *parent, SceneItemSelection::Type &type) { SceneItemTypeSelection dialog(parent, type); dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle")); if (dialog.exec() != DialogCode::Accepted) { return false; } type = static_cast( dialog._typeSelection->currentData().toInt()); return true; } } // namespace advss