From 4cae420ade805249f8dd8ce8dc7fe01f4e92c778 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:28:56 +0200 Subject: [PATCH] Add support for "any" selection in secene item selection --- data/locale/en-US.ini | 18 +- data/locale/fr-FR.ini | 10 +- data/locale/ja-JP.ini | 12 +- data/locale/pt-BR.ini | 14 +- data/locale/zh-CN.ini | 14 +- plugins/base/macro-action-media.cpp | 19 +- plugins/base/macro-action-scene-lock.cpp | 14 +- plugins/base/macro-action-scene-order.cpp | 28 +- plugins/base/macro-action-scene-transform.cpp | 14 +- .../base/macro-action-scene-visibility.cpp | 14 +- plugins/base/macro-action-transition.cpp | 21 +- plugins/base/macro-condition-scene-order.cpp | 211 +++++++++---- .../base/macro-condition-scene-transform.cpp | 163 +++++++++- .../base/macro-condition-scene-transform.hpp | 4 + .../base/macro-condition-scene-visibility.cpp | 97 ++++-- .../base/macro-condition-scene-visibility.hpp | 2 +- plugins/base/utils/scene-item-selection.cpp | 286 +++++++++++------- plugins/base/utils/scene-item-selection.hpp | 39 ++- 18 files changed, 722 insertions(+), 258 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 5dd3e046..f27b2033 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -2250,21 +2250,23 @@ AdvSceneSwitcher.setting.transform.width="Width" AdvSceneSwitcher.sceneItemSelection.configure="Configure scene item selection type" AdvSceneSwitcher.sceneItemSelection.type.sourceName="Source name" -AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceName.layout="{{nameConflictIndex}}{{sourceName}}" AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Variable name" -AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.layout="{{nameConflictIndex}}{{variable}}" AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Source name matches pattern" -AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.layout="{{nameConflictIndex}}matching{{pattern}}{{regex}}" AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="Sources in source group" -AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="Sources in{{sourceGroups}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.layout="{{nameConflictIndex}}Sources in{{sourceGroups}}" AdvSceneSwitcher.sceneItemSelection.type.sourceType="Sources of type" -AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="Source type{{nameConflictIndex}}{{sourceTypes}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout="Source type{{nameConflictIndex}}{{sourceTypes}}" AdvSceneSwitcher.sceneItemSelection.type.index="Source with index" -AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}source" +AdvSceneSwitcher.sceneItemSelection.type.index.layout="{{index}}source" AdvSceneSwitcher.sceneItemSelection.type.indexRange="Sources in index range" -AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="Sources from{{index}}to{{indexEnd}}" +AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout="Sources from{{index}}to{{indexEnd}}" AdvSceneSwitcher.sceneItemSelection.type.all="All sources" -AdvSceneSwitcher.sceneItemSelection.type.all.entry="All sources" +AdvSceneSwitcher.sceneItemSelection.type.all.layout="All sources" +AdvSceneSwitcher.sceneItemSelection.type.any="Any source" +AdvSceneSwitcher.sceneItemSelection.type.any.layout="Any source" AdvSceneSwitcher.sceneItemSelection.all="All" AdvSceneSwitcher.sceneItemSelection.any="Any" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index ed5036e1..e604782b 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -1107,15 +1107,15 @@ AdvSceneSwitcher.sceneItemSelection.configure="Configurer le type de sélection AdvSceneSwitcher.sceneItemSelection.type.sourceName="Nom de la source" AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Nom de la variable" AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Le nom de la source correspond au motif" -AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}correspondant à{{pattern}}{{regex}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.layout="{{nameConflictIndex}}correspondant à{{pattern}}{{regex}}" AdvSceneSwitcher.sceneItemSelection.type.sourceType="Sources de type" -AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="{{nameConflictIndex}}Type de source{{sourceTypes}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout="{{nameConflictIndex}}Type de source{{sourceTypes}}" AdvSceneSwitcher.sceneItemSelection.type.index="Source avec index" -AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}source" +AdvSceneSwitcher.sceneItemSelection.type.index.layout="{{index}}source" AdvSceneSwitcher.sceneItemSelection.type.indexRange="Sources dans la plage d'index" -AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="Sources de{{index}}à{{indexEnd}}" +AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout="Sources de{{index}}à{{indexEnd}}" AdvSceneSwitcher.sceneItemSelection.type.all="Toutes les sources" -AdvSceneSwitcher.sceneItemSelection.type.all.entry="Toutes les sources" +AdvSceneSwitcher.sceneItemSelection.type.all.layout="Toutes les sources" AdvSceneSwitcher.sceneItemSelection.all="Tout" AdvSceneSwitcher.sceneItemSelection.any="N'importe lequel" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index adfca03b..e3285c0a 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -2237,21 +2237,19 @@ AdvSceneSwitcher.setting.transform.width="幅" AdvSceneSwitcher.sceneItemSelection.configure="シーン項目選択タイプの設定" AdvSceneSwitcher.sceneItemSelection.type.sourceName="ソース名" -; AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}" AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="変数名" -; AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}" AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="ソース名に一致するパターン" ; AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}matching{{pattern}}{{regex}}" AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="ソースグループ内のソース数" -AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="{{sourceGroups}}内のソース数" +AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.layout="{{nameConflictIndex}}{{sourceGroups}}内のソース数" AdvSceneSwitcher.sceneItemSelection.type.sourceType="ソースの種類" -AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="ソースタイプ{{nameConflictIndex}}{{sourceTypes}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout="ソースタイプ{{nameConflictIndex}}{{sourceTypes}}" AdvSceneSwitcher.sceneItemSelection.type.index="ソースインデックス(上から順)" -AdvSceneSwitcher.sceneItemSelection.type.index.entry="ソース{{index}}" +AdvSceneSwitcher.sceneItemSelection.type.index.layout="ソース{{index}}" AdvSceneSwitcher.sceneItemSelection.type.indexRange="複数のソースインデックス(上から順)" -AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="ソースの{{index}}から{{indexEnd}}まで" +AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout="ソースの{{index}}から{{indexEnd}}まで" AdvSceneSwitcher.sceneItemSelection.type.all="全てのソース" -AdvSceneSwitcher.sceneItemSelection.type.all.entry="全てのソース" +AdvSceneSwitcher.sceneItemSelection.type.all.layout="全てのソース" AdvSceneSwitcher.sceneItemSelection.all="全て" AdvSceneSwitcher.sceneItemSelection.any="いずれか" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index 9c66b5dc..0d857287 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -1851,19 +1851,19 @@ AdvSceneSwitcher.setting.transform.width="Largura" AdvSceneSwitcher.sceneItemSelection.configure="Configurar tipo de seleção de item de cena" AdvSceneSwitcher.sceneItemSelection.type.sourceName="Nome da fonte" -AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceName.layout="{{nameConflictIndex}}{{sourceName}}" AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="Nome da variável" -AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.layout="{{nameConflictIndex}}{{variable}}" AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="Nome da fonte corresponde ao padrão" -AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}correspondendo{{pattern}}{{regex}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.layout="{{nameConflictIndex}}correspondendo{{pattern}}{{regex}}" AdvSceneSwitcher.sceneItemSelection.type.sourceType="Fontes do tipo" -AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="Tipo de fonte{{nameConflictIndex}}{{sourceTypes}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout="Tipo de fonte{{nameConflictIndex}}{{sourceTypes}}" AdvSceneSwitcher.sceneItemSelection.type.index="Fonte com índice" -AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}fonte" +AdvSceneSwitcher.sceneItemSelection.type.index.layout="{{index}}fonte" AdvSceneSwitcher.sceneItemSelection.type.indexRange="Fontes no intervalo de índices" -AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="Fontes de{{index}}para{{indexEnd}}" +AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout="Fontes de{{index}}para{{indexEnd}}" AdvSceneSwitcher.sceneItemSelection.type.all="Todas as fontes" -AdvSceneSwitcher.sceneItemSelection.type.all.entry="Todas as fontes" +AdvSceneSwitcher.sceneItemSelection.type.all.layout="Todas as fontes" AdvSceneSwitcher.sceneItemSelection.all="Todas" AdvSceneSwitcher.sceneItemSelection.any="Qualquer" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index efc632ea..adc4793a 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -2095,21 +2095,19 @@ AdvSceneSwitcher.setting.transform.width="宽度" AdvSceneSwitcher.sceneItemSelection.configure="配置场景项目选择类型" AdvSceneSwitcher.sceneItemSelection.type.sourceName="源名称" -AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry="{{nameConflictIndex}}{{sourceName}}" AdvSceneSwitcher.sceneItemSelection.type.sourceVariable="变量名称" -AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry="{{nameConflictIndex}}{{variable}}" AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern="源名称与匹配相符" -AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry="{{nameConflictIndex}}匹配{{pattern}}{{regex}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.layout="{{nameConflictIndex}}匹配{{pattern}}{{regex}}" AdvSceneSwitcher.sceneItemSelection.type.sourceGroup="源分组中的来源" -AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry="{{sourceGroups}}中的源" +AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.layout="{{nameConflictIndex}}{{sourceGroups}}中的源" AdvSceneSwitcher.sceneItemSelection.type.sourceType="源类型" -AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry="源类型{{nameConflictIndex}}{{sourceTypes}}" +AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout="源类型{{nameConflictIndex}}{{sourceTypes}}" AdvSceneSwitcher.sceneItemSelection.type.index="附有索引的源" -AdvSceneSwitcher.sceneItemSelection.type.index.entry="{{index}}源" +AdvSceneSwitcher.sceneItemSelection.type.index.layout="{{index}}源" AdvSceneSwitcher.sceneItemSelection.type.indexRange="索引范围内的源" -AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry="从{{index}}到{{indexEnd}}的源" +AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout="从{{index}}到{{indexEnd}}的源" AdvSceneSwitcher.sceneItemSelection.type.all="所有源" -AdvSceneSwitcher.sceneItemSelection.type.all.entry="所有源" +AdvSceneSwitcher.sceneItemSelection.type.all.layout="所有源" AdvSceneSwitcher.sceneItemSelection.all="全部" AdvSceneSwitcher.sceneItemSelection.any="任何" diff --git a/plugins/base/macro-action-media.cpp b/plugins/base/macro-action-media.cpp index 4d12f1a4..70f84726 100644 --- a/plugins/base/macro-action-media.cpp +++ b/plugins/base/macro-action-media.cpp @@ -237,7 +237,24 @@ MacroActionMediaEdit::MacroActionMediaEdit( obs_module_text( "AdvSceneSwitcher.action.media.seek.percentage.label"))), _sources(new SourceSelectionWidget(this, getMediaSourcesList, true)), - _sceneItems(new SceneItemSelectionWidget(parent, false)), + _sceneItems(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + // All instances of a media source will be in the same state. + // So, for example, restarting one instance of a source will + // automatically restart all other instances of that source, + // too. + // Thus, we hide the name clash resolution options + SceneItemSelectionWidget::NameClashMode::HIDE)), _scenes(new SceneSelectionWidget(this, true, false, true, true, true)) { populateActionSelection(_actions); diff --git a/plugins/base/macro-action-scene-lock.cpp b/plugins/base/macro-action-scene-lock.cpp index fa1bdffe..978eec1d 100644 --- a/plugins/base/macro-action-scene-lock.cpp +++ b/plugins/base/macro-action-scene-lock.cpp @@ -113,7 +113,19 @@ MacroActionSceneLockEdit::MacroActionSceneLockEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, true, true)), - _sources(new SceneItemSelectionWidget(parent)), + _sources(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), _actions(new QComboBox()) { populateActionSelection(_actions); diff --git a/plugins/base/macro-action-scene-order.cpp b/plugins/base/macro-action-scene-order.cpp index 5c2a40a0..9b28c0d1 100644 --- a/plugins/base/macro-action-scene-order.cpp +++ b/plugins/base/macro-action-scene-order.cpp @@ -276,8 +276,32 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, false, true)), - _sources(new SceneItemSelectionWidget(this)), - _sources2(new SceneItemSelectionWidget(this)), + _sources(new SceneItemSelectionWidget( + this, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), + _sources2(new SceneItemSelectionWidget( + this, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), _actions(new QComboBox(this)), _position(new QSpinBox(this)) { diff --git a/plugins/base/macro-action-scene-transform.cpp b/plugins/base/macro-action-scene-transform.cpp index 76498df5..6d056303 100644 --- a/plugins/base/macro-action-scene-transform.cpp +++ b/plugins/base/macro-action-scene-transform.cpp @@ -400,7 +400,19 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, false, true)), - _sources(new SceneItemSelectionWidget(parent)), + _sources(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), _action(new QComboBox()), _rotation(new VariableDoubleSpinBox()), _getTransform(new QPushButton(obs_module_text( diff --git a/plugins/base/macro-action-scene-visibility.cpp b/plugins/base/macro-action-scene-visibility.cpp index 6553cb65..108b6104 100644 --- a/plugins/base/macro-action-scene-visibility.cpp +++ b/plugins/base/macro-action-scene-visibility.cpp @@ -128,7 +128,19 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, true, true)), - _sources(new SceneItemSelectionWidget(parent)), + _sources(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), _actions(new QComboBox()) { populateActionSelection(_actions); diff --git a/plugins/base/macro-action-transition.cpp b/plugins/base/macro-action-transition.cpp index 76618e36..49cddd16 100644 --- a/plugins/base/macro-action-transition.cpp +++ b/plugins/base/macro-action-transition.cpp @@ -237,7 +237,19 @@ MacroActionTransitionEdit::MacroActionTransitionEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _actions(new QComboBox), - _sources(new SceneItemSelectionWidget(parent, false)), + _sources(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + }, + SceneItemSelectionWidget::NameClashMode::ALL)), _scenes(new SceneSelectionWidget(this, true, false, false, true)), _setTransition(new QCheckBox), _setDuration(new QCheckBox), @@ -268,7 +280,7 @@ MacroActionTransitionEdit::MacroActionTransitionEdit( QWidget::connect(_setDuration, SIGNAL(stateChanged(int)), this, SLOT(SetDurationChanged(int))); - std::unordered_map widgetPlaceholders = { + const std::unordered_map widgetPlaceholders = { {"{{type}}", _actions}, {"{{sources}}", _sources}, {"{{scenes}}", _scenes}, @@ -277,7 +289,8 @@ MacroActionTransitionEdit::MacroActionTransitionEdit( {"{{setTransition}}", _setTransition}, {"{{setDuration}}", _setDuration}, }; - QHBoxLayout *typeLayout = new QHBoxLayout; + + auto typeLayout = new QHBoxLayout; PlaceWidgets(obs_module_text( "AdvSceneSwitcher.action.transition.entry.line1"), typeLayout, widgetPlaceholders); @@ -287,7 +300,7 @@ MacroActionTransitionEdit::MacroActionTransitionEdit( PlaceWidgets(obs_module_text( "AdvSceneSwitcher.action.transition.entry.line3"), _durationLayout, widgetPlaceholders); - QVBoxLayout *mainLayout = new QVBoxLayout; + auto mainLayout = new QVBoxLayout; mainLayout->addLayout(typeLayout); mainLayout->addLayout(_transitionLayout); mainLayout->addLayout(_durationLayout); diff --git a/plugins/base/macro-condition-scene-order.cpp b/plugins/base/macro-condition-scene-order.cpp index 55077185..6e395729 100644 --- a/plugins/base/macro-condition-scene-order.cpp +++ b/plugins/base/macro-condition-scene-order.cpp @@ -63,36 +63,100 @@ static std::vector getSceneItemPositions(std::vector &items, return positions; } -static bool isAbove(std::vector &pos1, std::vector &pos2) +static bool anyAboveAny(const std::vector &a, const std::vector &b) { - if (pos1.empty() || pos2.empty()) { + for (int ai : a) + for (int bi : b) + if (ai > bi) + return true; + return false; +} + +static bool anyAboveAll(const std::vector &a, const std::vector &b) +{ + if (b.empty()) return false; - } - for (int i : pos1) { - for (int j : pos2) { - if (i <= j) { - return false; + int max_b = *std::max_element(b.begin(), b.end()); + for (int ai : a) + if (ai > max_b) + return true; + return false; +} + +static bool allAboveAny(const std::vector &a, const std::vector &b) +{ + for (int ai : a) { + bool found = false; + for (int bi : b) { + if (ai > bi) { + found = true; + break; } } + if (!found) + return false; } return true; } -static bool isBelow(std::vector &pos1, std::vector &pos2) +static bool allAboveAll(const std::vector &a, const std::vector &b) { - if (pos1.empty() || pos2.empty()) { + if (b.empty()) return false; - } - for (int i : pos1) { - for (int j : pos2) { - if (i >= j) { - return false; + int max_b = *std::max_element(b.begin(), b.end()); + for (int ai : a) + if (ai <= max_b) + return false; + return true; +} + +static bool anyBelowAny(const std::vector &a, const std::vector &b) +{ + for (int ai : a) + for (int bi : b) + if (ai < bi) + return true; + return false; +} + +static bool anyBelowAll(const std::vector &a, const std::vector &b) +{ + if (b.empty()) + return false; + int min_b = *std::min_element(b.begin(), b.end()); + for (int ai : a) + if (ai < min_b) + return true; + return false; +} + +static bool allBelowAny(const std::vector &a, const std::vector &b) +{ + for (int ai : a) { + bool found = false; + for (int bi : b) { + if (ai < bi) { + found = true; + break; } } + if (!found) + return false; } return true; } +static bool allBelowAll(const std::vector &a, const std::vector &b) +{ + if (b.empty()) + return false; + int min_b = *std::min_element(b.begin(), b.end()); + for (int ai : a) + if (ai >= min_b) + return false; + return true; +} + bool MacroConditionSceneOrder::CheckCondition() { auto items1 = _source.GetSceneItems(_scene); @@ -101,24 +165,58 @@ bool MacroConditionSceneOrder::CheckCondition() } auto items2 = _source2.GetSceneItems(_scene); - auto s = obs_weak_source_get_source(_scene.GetScene(false)); + auto s = OBSGetStrongRef(_scene.GetScene(false)); auto scene = obs_scene_from_source(s); auto positions1 = getSceneItemPositions(items1, scene); auto positions2 = getSceneItemPositions(items2, scene); - bool ret = false; + const bool pos1IsAnyCheck = _source.IsSelectionOfTypeAny(); + const bool pos2IsAnyCheck = _source2.IsSelectionOfTypeAny(); switch (_condition) { case Condition::ABOVE: - ret = isAbove(positions1, positions2); + if (pos1IsAnyCheck) { + if (pos2IsAnyCheck) { + return anyAboveAny(positions1, positions2); + } else { + return anyAboveAll(positions1, positions2); + } + } else { + if (pos2IsAnyCheck) { + return allAboveAny(positions1, positions2); + } else { + return allAboveAll(positions1, positions2); + } + } break; case Condition::BELOW: - ret = isBelow(positions1, positions2); + if (pos1IsAnyCheck) { + if (pos2IsAnyCheck) { + return anyBelowAny(positions1, positions2); + } else { + return anyBelowAll(positions1, positions2); + } + } else { + if (pos2IsAnyCheck) { + return allBelowAny(positions1, positions2); + } else { + return allBelowAll(positions1, positions2); + } + } break; case Condition::POSITION: - for (int p : positions1) { - if (p == _position) { - ret = true; + if (pos1IsAnyCheck) { + for (int p : positions1) { + if (p == _position) { + return true; + } + } + } else { + if (positions1.size() == 1) { + return positions1[0] == _position; + } else { + // Multiple scene items can't have the same pos + return false; } } break; @@ -126,8 +224,7 @@ bool MacroConditionSceneOrder::CheckCondition() break; } - obs_source_release(s); - return ret; + return false; } bool MacroConditionSceneOrder::Save(obs_data_t *obj) const @@ -199,23 +296,38 @@ MacroConditionSceneOrderEdit::MacroConditionSceneOrderEdit( : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, false, true)), _conditions(new QComboBox()), - _sources(new SceneItemSelectionWidget(parent)), - _sources2(new SceneItemSelectionWidget(parent)), + _sources(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + SceneItemSelection::Type::ANY, + }, + SceneItemSelectionWidget::NameClashMode::ANY_AND_ALL)), + _sources2(new SceneItemSelectionWidget( + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ANY, + }, + SceneItemSelectionWidget::NameClashMode::ANY_AND_ALL)), _position(new VariableSpinBox()), _posInfo(new QLabel(obs_module_text( "AdvSceneSwitcher.condition.sceneOrder.positionInfo"))) { populateConditionSelection(_conditions); - if (entryData.get()) { - if (entryData->_condition == - MacroConditionSceneOrder::Condition::POSITION) { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ANY); - } else { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ALL); - } - } QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), this, SLOT(SceneChanged(const SceneSelection &))); @@ -238,18 +350,18 @@ MacroConditionSceneOrderEdit::MacroConditionSceneOrderEdit( this, SLOT(PositionChanged(const NumberVariable &))); auto entryLayout = new QHBoxLayout(); - std::unordered_map widgetPlaceholders = { - {"{{scenes}}", _scenes}, {"{{sources}}", _sources}, - {"{{sources2}}", _sources2}, {"{{conditions}}", _conditions}, - {"{{position}}", _position}, - }; PlaceWidgets( obs_module_text("AdvSceneSwitcher.condition.sceneOrder.entry"), - entryLayout, widgetPlaceholders); - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addLayout(entryLayout); - mainLayout->addWidget(_posInfo); - setLayout(mainLayout); + entryLayout, + {{"{{scenes}}", _scenes}, + {"{{sources}}", _sources}, + {"{{sources2}}", _sources2}, + {"{{conditions}}", _conditions}, + {"{{position}}", _position}}); + auto layout = new QVBoxLayout; + layout->addLayout(entryLayout); + layout->addWidget(_posInfo); + setLayout(layout); _entryData = entryData; UpdateEntryData(); @@ -291,15 +403,6 @@ void MacroConditionSceneOrderEdit::ConditionChanged(int index) } SetWidgetVisibility(_entryData->_condition == MacroConditionSceneOrder::Condition::POSITION); - if (_entryData->_condition == - MacroConditionSceneOrder::Condition::POSITION) { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ANY); - } else { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ALL); - } - emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); } diff --git a/plugins/base/macro-condition-scene-transform.cpp b/plugins/base/macro-condition-scene-transform.cpp index b0bd1a5c..9470c44a 100644 --- a/plugins/base/macro-condition-scene-transform.cpp +++ b/plugins/base/macro-condition-scene-transform.cpp @@ -56,6 +56,22 @@ static bool doesTransformOfAnySceneItemMatch( return ret; } +static bool doesTransformOfAllSceneItemsMatch( + const std::vector &items, const std::string &jsonCompare, + const RegexConfig ®ex, std::string &newVariable) +{ + bool ret = true; + std::string json; + for (const auto &item : items) { + json = GetSceneItemTransform(item); + if (!MatchJson(json, jsonCompare, regex)) { + ret = false; + } + } + newVariable = json; + return ret; +} + static bool didTransformOfAnySceneItemChange(const std::vector &items, std::vector &previousTransform, @@ -81,19 +97,57 @@ didTransformOfAnySceneItemChange(const std::vector &items, return ret; } +static bool +didTransformOfAllSceneItemsChange(const std::vector &items, + std::vector &previousTransform, + std::string &newVariable) +{ + const auto numItems = items.size(); + if (previousTransform.size() != numItems) { + previousTransform.resize(numItems); + return false; + } + + std::string json; + RegexConfig regex; + bool ret = true; + for (size_t idx = 0; idx < numItems; ++idx) { + auto const &item = items[idx]; + json = GetSceneItemTransform(item); + if (MatchJson(json, previousTransform[idx], regex)) { + ret = false; + } else { + previousTransform[idx] = json; + } + } + + newVariable = json; + return ret; +} + bool MacroConditionSceneTransform::CheckAllSettings( const std::vector &items) { std::string newVariable = ""; + + const bool checkAny = _source.IsSelectionOfTypeAny(); + bool ret = false; switch (_condition) { case Condition::MATCHES: - ret = doesTransformOfAnySceneItemMatch(items, _transformString, - _regex, newVariable); + ret = checkAny ? doesTransformOfAnySceneItemMatch( + items, _transformString, _regex, + newVariable) + : doesTransformOfAllSceneItemsMatch( + items, _transformString, _regex, + newVariable); break; case Condition::CHANGED: - ret = didTransformOfAnySceneItemChange( - items, _previousTransform, newVariable); + ret = checkAny + ? didTransformOfAnySceneItemChange( + items, _previousTransform, newVariable) + : didTransformOfAllSceneItemsChange( + items, _previousTransform, newVariable); break; default: @@ -200,16 +254,97 @@ void MacroConditionSceneTransform::SetTempVarHelper( bool MacroConditionSceneTransform::CheckSingleSetting( const std::vector &items) { - if (_condition == Condition::CHANGED) { - return AnySceneItemTransformSettingChanged(items); + const bool checkAny = _source.IsSelectionOfTypeAny(); + + switch (_condition) { + case Condition::MATCHES: + return checkAny ? AnySceneItemTransformSettingMatches(items) + : AllSceneItemsTransformSettingMatch(items); + case Condition::CHANGED: + return checkAny ? AnySceneItemTransformSettingChanged(items) + : AllSceneItemsTransformSettingChanged(items); + break; + default: + break; } - return AnySceneItemTransformSettingMatches(items); + + return false; +} + +bool MacroConditionSceneTransform::AllSceneItemsTransformSettingChanged( + const std::vector &items) +{ + if (_previousSettingValues.size() < items.size()) { + _previousSettingValues.resize(items.size()); + return false; + } + + if (_setting.GetID().empty()) { + return false; + } + + bool ret = true; + std::vector varValues; + + for (size_t i = 0; i < items.size(); i++) { + const auto &item = items[i]; + auto &previousValue = _previousSettingValues[i]; + + const auto currentValue = + GetTransformSettingValue(item, _setting); + if (!currentValue) { + continue; + } + varValues.emplace_back(*currentValue); + + if (*currentValue == previousValue) { + ret = false; + } + previousValue = *currentValue; + } + + SetTempVarHelper(varValues); + return ret; +} + +bool MacroConditionSceneTransform::AllSceneItemsTransformSettingMatch( + const std::vector &items) +{ + if (_setting.GetID().empty()) { + return false; + } + + bool ret = true; + std::vector varValues; + + for (const auto &item : items) { + const auto currentValue = + GetTransformSettingValue(item, _setting); + if (!currentValue) { + continue; + } + try { + if (!compareValue(_compare, std::stod(*currentValue), + std::stod(_singleSetting))) { + ret = false; + } + } catch (std::invalid_argument &) { + } catch (std::out_of_range &) { + } + + varValues.emplace_back(*currentValue); + } + + SetTempVarHelper(varValues); + return ret; } bool MacroConditionSceneTransform::CheckCondition() { auto items = _source.GetSceneItems(_scene); if (items.empty()) { + _previousSettingValues.clear(); + _previousTransform.clear(); return false; } @@ -335,7 +470,19 @@ MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit( : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, false, true)), _sources(new SceneItemSelectionWidget( - parent, true, SceneItemSelectionWidget::Placeholder::ANY)), + parent, + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + SceneItemSelection::Type::ANY, + }, + SceneItemSelectionWidget::NameClashMode::ANY_AND_ALL)), _settingsType(new QComboBox()), _compare(new QComboBox()), _conditions(new QComboBox()), diff --git a/plugins/base/macro-condition-scene-transform.hpp b/plugins/base/macro-condition-scene-transform.hpp index d888b649..503d3afa 100644 --- a/plugins/base/macro-condition-scene-transform.hpp +++ b/plugins/base/macro-condition-scene-transform.hpp @@ -45,6 +45,10 @@ private: bool CheckAllSettings(const std::vector &); bool CheckSingleSetting(const std::vector &); bool + AllSceneItemsTransformSettingChanged(const std::vector &); + bool + AllSceneItemsTransformSettingMatch(const std::vector &); + bool AnySceneItemTransformSettingChanged(const std::vector &); bool AnySceneItemTransformSettingMatches(const std::vector &); diff --git a/plugins/base/macro-condition-scene-visibility.cpp b/plugins/base/macro-condition-scene-visibility.cpp index 261a5fce..7073d51c 100644 --- a/plugins/base/macro-condition-scene-visibility.cpp +++ b/plugins/base/macro-condition-scene-visibility.cpp @@ -36,6 +36,15 @@ static bool areAllSceneItemsShown(const std::vector &items) return ret; } +static bool isAnySceneItemShown(const std::vector &items) +{ + bool ret = false; + for (const auto &item : items) { + ret |= obs_sceneitem_visible(item); + } + return ret; +} + static bool areAllSceneItemsHidden(const std::vector &items) { bool ret = true; @@ -47,6 +56,15 @@ static bool areAllSceneItemsHidden(const std::vector &items) return ret; } +static bool isAnySceneItemHidden(const std::vector &items) +{ + bool ret = false; + for (const auto &item : items) { + ret |= (!obs_sceneitem_visible(item)); + } + return ret; +} + static bool didVisibilityOfAnySceneItemsChange(const std::vector &items, std::vector &previousVisibility) @@ -66,21 +84,53 @@ didVisibilityOfAnySceneItemsChange(const std::vector &items, return ret; } +static bool +didVisibilityOfAllSceneItemsChange(const std::vector &items, + std::vector &previousVisibility) +{ + std::vector currentVisibility; + for (const auto &item : items) { + currentVisibility.emplace_back(obs_sceneitem_visible(item)); + } + + if (previousVisibility.size() != currentVisibility.size()) { + previousVisibility = currentVisibility; + return false; + } + + for (std::size_t i = 0; i < currentVisibility.size(); i++) { + if (currentVisibility[i] == previousVisibility[i]) { + previousVisibility = currentVisibility; + return false; + } + } + + previousVisibility = currentVisibility; + return true; +} + bool MacroConditionSceneVisibility::CheckCondition() { auto items = _source.GetSceneItems(_scene); if (items.empty()) { + _previousVisibility.clear(); return false; } + const bool checkAny = _source.IsSelectionOfTypeAny(); + switch (_condition) { case Condition::SHOWN: - return areAllSceneItemsShown(items); + return checkAny ? isAnySceneItemShown(items) + : areAllSceneItemsShown(items); case Condition::HIDDEN: - return areAllSceneItemsHidden(items); + return checkAny ? isAnySceneItemHidden(items) + : areAllSceneItemsHidden(items); case Condition::CHANGED: - return didVisibilityOfAnySceneItemsChange(items, - _previousVisibilty); + return checkAny ? didVisibilityOfAnySceneItemsChange( + items, _previousVisibility) + : didVisibilityOfAllSceneItemsChange( + items, _previousVisibility); default: break; } @@ -94,7 +144,7 @@ bool MacroConditionSceneVisibility::Save(obs_data_t *obj) const _scene.Save(obj); _source.Save(obj); obs_data_set_int(obj, "condition", static_cast(_condition)); - + obs_data_set_int(obj, "version", 1); return true; } @@ -111,6 +161,13 @@ bool MacroConditionSceneVisibility::Load(obs_data_t *obj) _scene.Load(obj); _source.Load(obj); _condition = static_cast(obs_data_get_int(obj, "condition")); + + if (!obs_data_has_user_value(obj, "version") && + _condition == Condition::CHANGED && + _source.GetType() == SceneItemSelection::Type::ALL) { + _source.SetType(SceneItemSelection::Type::ANY); + } + return true; } @@ -150,17 +207,15 @@ MacroConditionSceneVisibilityEdit::MacroConditionSceneVisibilityEdit( QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); - std::unordered_map widgetPlaceholders = { - {"{{sources}}", _sources}, - {"{{scenes}}", _scenes}, - {"{{conditions}}", _conditions}, - }; - QHBoxLayout *mainLayout = new QHBoxLayout; + auto layout = new QHBoxLayout; PlaceWidgets( obs_module_text( "AdvSceneSwitcher.condition.sceneVisibility.entry"), - mainLayout, widgetPlaceholders); - setLayout(mainLayout); + layout, + {{"{{sources}}", _sources}, + {"{{scenes}}", _scenes}, + {"{{conditions}}", _conditions}}); + setLayout(layout); _entryData = entryData; UpdateEntryData(); @@ -191,14 +246,6 @@ void MacroConditionSceneVisibilityEdit::ConditionChanged(int index) GUARD_LOADING_AND_LOCK(); _entryData->_condition = static_cast(index); - if (_entryData->_condition == - MacroConditionSceneVisibility::Condition::CHANGED) { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ANY, false); - } else { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ALL, false); - } } void MacroConditionSceneVisibilityEdit::UpdateEntryData() @@ -209,14 +256,6 @@ void MacroConditionSceneVisibilityEdit::UpdateEntryData() _conditions->setCurrentIndex(static_cast(_entryData->_condition)); _scenes->SetScene(_entryData->_scene); - if (_entryData->_condition == - MacroConditionSceneVisibility::Condition::CHANGED) { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ANY, false); - } else { - _sources->SetPlaceholderType( - SceneItemSelectionWidget::Placeholder::ALL, false); - } _sources->SetSceneItem(_entryData->_source); } diff --git a/plugins/base/macro-condition-scene-visibility.hpp b/plugins/base/macro-condition-scene-visibility.hpp index e7649611..36f38e27 100644 --- a/plugins/base/macro-condition-scene-visibility.hpp +++ b/plugins/base/macro-condition-scene-visibility.hpp @@ -31,7 +31,7 @@ public: Condition _condition = Condition::SHOWN; private: - std::vector _previousVisibilty; + std::vector _previousVisibility; static bool _registered; static const std::string id; diff --git a/plugins/base/utils/scene-item-selection.cpp b/plugins/base/utils/scene-item-selection.cpp index 505b76af..dee023db 100644 --- a/plugins/base/utils/scene-item-selection.cpp +++ b/plugins/base/utils/scene-item-selection.cpp @@ -6,9 +6,18 @@ #include "ui-helpers.hpp" #include +#include + +using NameClashMode = advss::SceneItemSelectionWidget::NameClashMode; +using ConflictIndex = std::variant; + +Q_DECLARE_METATYPE(NameClashMode) +Q_DECLARE_METATYPE(ConflictIndex); namespace advss { +using NameConflictSelection = SceneItemSelection::NameConflictSelection; + constexpr std::string_view typeSaveName = "type"; constexpr std::string_view itemSaveName = "item"; constexpr std::string_view indexSaveName = "index"; @@ -17,25 +26,6 @@ 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 { @@ -332,7 +322,7 @@ void SceneItemSelection::ResolveVariables() } } -void SceneItemSelection::ReduceBadedOnIndexSelection( +void SceneItemSelection::ReduceBasedOnIndexSelection( std::vector &items) const { if (_nameConflictSelectionType == @@ -368,7 +358,7 @@ std::vector SceneItemSelection::GetSceneItemsByName( name = GetWeakSourceName(_source); } auto items = getSceneItemsWithName(scene, name); - ReduceBadedOnIndexSelection(items); + ReduceBasedOnIndexSelection(items); return items; } @@ -380,7 +370,7 @@ std::vector SceneItemSelection::GetSceneItemsByPattern( NamePatternMatchData data{_pattern, _regex}; obs_scene_enum_items(scene, getSceneItemsByPatternHelper, &data); obs_source_release(s); - ReduceBadedOnIndexSelection(data.items); + ReduceBasedOnIndexSelection(data.items); return data.items; } @@ -405,7 +395,7 @@ std::vector SceneItemSelection::GetSceneItemsByType( GroupData data{_sourceType}; obs_scene_enum_items(scene, getSceneItemsOfType, &data); obs_source_release(s); - ReduceBadedOnIndexSelection(data.items); + ReduceBasedOnIndexSelection(data.items); return data.items; } @@ -475,6 +465,7 @@ SceneItemSelection::GetSceneItems(const SceneSelection &sceneSelection) const case Type::INDEX_RANGE: return GetSceneItemsByIdx(sceneSelection); case Type::ALL: + case Type::ANY: return GetAllSceneItems(sceneSelection); default: break; @@ -483,6 +474,31 @@ SceneItemSelection::GetSceneItems(const SceneSelection &sceneSelection) const return {}; } +bool SceneItemSelection::IsSelectionOfTypeAny() const +{ + if (_type == Type::ANY) { + return true; + } + + if (_type == Type::ALL) { + return false; + } + + if (_type == Type::INDEX_RANGE) { + return false; + } + + if (_nameConflictSelectionType == NameConflictSelection::ANY) { + return true; + } + + if (_nameConflictSelectionType == NameConflictSelection::ALL) { + return false; + } + + return false; +} + std::string SceneItemSelection::ToString(bool resolve) const { switch (_type) { @@ -509,7 +525,7 @@ std::string SceneItemSelection::ToString(bool resolve) const if (resolve) { return std::string(obs_module_text( "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup")) + - " \"" + std::string(_sourceType) + "\""; + " \"" + GetWeakSourceName(_source) + "\""; } return _sourceType; case Type::SOURCE_TYPE: @@ -595,9 +611,35 @@ void SceneItemSelectionWidget::PopulateItemSelection() _sources->setCurrentIndex(-1); } -static inline void populateMessageTypeSelection(QComboBox *list) +static inline void populateMessageTypeSelection( + QComboBox *list, + const std::vector &allowedTypes) { - for (const auto &[type, name] : types) { + 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"}, + {SceneItemSelection::Type::ANY, + "AdvSceneSwitcher.sceneItemSelection.type.any"}, + }; + + for (const auto &type : allowedTypes) { + const auto it = types.find(type); + assert(it != types.end()); + const auto &[_, name] = *it; list->addItem(obs_module_text(name.c_str()), static_cast(type)); } @@ -674,9 +716,10 @@ static void populateSourceGroupSelection(QComboBox *list) list->setCurrentIndex(0); } -SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent, - bool showAll, - Placeholder type) +SceneItemSelectionWidget::SceneItemSelectionWidget( + QWidget *parent, + const std::vector &selections, + NameClashMode mode) : QWidget(parent), _controlsLayout(new QHBoxLayout), _sources(new FilterComboBox( @@ -691,9 +734,16 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent, _pattern(new VariableLineEdit(this)), _regex(new RegexConfigWidget(this, false)), _changeType(new QPushButton(this)), - _hasPlaceholderEntry(showAll), - _placeholder(type) + _nameClashMode(mode), + _selectionTypes(selections) { + static bool setupDone = false; + if (setupDone) { + setupDone = true; + qRegisterMetaType("NameClashMode"); + qRegisterMetaType("ConflictIndex"); + } + _sources->setSizeAdjustPolicy(QComboBox::AdjustToContents); _sourceGroups->setSizeAdjustPolicy(QComboBox::AdjustToContents); _nameConflictIndex->setSizeAdjustPolicy(QComboBox::AdjustToContents); @@ -755,11 +805,6 @@ SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent, 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( @@ -779,19 +824,20 @@ void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item) 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; - } + auto type = item._nameConflictSelectionType; + + if (type == NameConflictSelection::INDIVIDUAL) { + const int selection = item._nameConflictSelectionIndex; + const int idx = _nameConflictIndex->findData( + QVariant::fromValue(ConflictIndex(selection))); + _nameConflictIndex->setCurrentIndex(idx); + } else { + const auto selection = type == NameConflictSelection::ANY + ? NameClashMode::ANY + : NameClashMode::ALL; + const int idx = _nameConflictIndex->findData( + QVariant::fromValue(ConflictIndex(selection))); _nameConflictIndex->setCurrentIndex(idx); - break; } SetWidgetVisibility(); @@ -812,24 +858,6 @@ void SceneItemSelectionWidget::SetScene(const SceneSelection &s) 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); @@ -873,9 +901,8 @@ void SceneItemSelectionWidget::SetNameConflictVisibility() } case SceneItemSelection::Type::SOURCE_GROUP: - // There cannot be a name conflict with source groups - _nameConflictIndex->hide(); - return; + sceneItemCount = _currentSelection.GetSceneItems(_scene).size(); + break; case SceneItemSelection::Type::SOURCE_NAME_PATTERN: case SceneItemSelection::Type::SOURCE_TYPE: sceneItemCount = GetSceneItemCount(_scene.GetScene(false)); @@ -883,6 +910,7 @@ void SceneItemSelectionWidget::SetNameConflictVisibility() case SceneItemSelection::Type::INDEX: case SceneItemSelection::Type::INDEX_RANGE: case SceneItemSelection::Type::ALL: + case SceneItemSelection::Type::ANY: break; } @@ -951,24 +979,30 @@ void SceneItemSelectionWidget::NameConflictIndexChanged(int idx) 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; + QVariant data = _nameConflictIndex->currentData(Qt::UserRole); + if (!data.canConvert()) { + return; + } + + const auto setSelection = [this](int val, NameConflictSelection type) { + _currentSelection._nameConflictSelectionIndex = val; + _currentSelection._nameConflictSelectionType = type; + }; + + const auto visit = [this, &setSelection](auto &&val) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + setSelection(val, NameConflictSelection::INDIVIDUAL); + } else if constexpr (std::is_same_v) { + setSelection(0, val == NameClashMode::ANY + ? NameConflictSelection::ANY + : NameConflictSelection::ALL); } - } - if (_hasPlaceholderEntry && idx > 0) { - _currentSelection._nameConflictSelectionIndex -= 1; - _currentSelection._nameConflictSelectionType = - SceneItemSelection::NameConflictSelection::INDIVIDUAL; - } + }; + + const auto variant = data.value(); + std::visit(visit, variant); + emit SceneItemChanged(_currentSelection); } @@ -999,7 +1033,7 @@ void SceneItemSelectionWidget::SourceTypeChanged(const QString &text) void SceneItemSelectionWidget::ChangeType() { bool accepted = SceneItemTypeSelection::AskForSettings( - this, _currentSelection._type); + this, _currentSelection._type, _selectionTypes); if (!accepted) { return; } @@ -1012,18 +1046,42 @@ void SceneItemSelectionWidget::ChangeType() 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")); - } + + switch (_nameClashMode) { + case NameClashMode::NONE: + break; + case NameClashMode::ALL: + _nameConflictIndex->addItem( + obs_module_text( + "AdvSceneSwitcher.sceneItemSelection.all"), + QVariant::fromValue(ConflictIndex(NameClashMode::ALL))); + break; + case NameClashMode::ANY: + _nameConflictIndex->addItem( + obs_module_text( + "AdvSceneSwitcher.sceneItemSelection.any"), + QVariant::fromValue(ConflictIndex(NameClashMode::ANY))); + break; + case NameClashMode::ANY_AND_ALL: + _nameConflictIndex->addItem( + obs_module_text( + "AdvSceneSwitcher.sceneItemSelection.any"), + QVariant::fromValue(ConflictIndex(NameClashMode::ANY))); + _nameConflictIndex->addItem( + obs_module_text( + "AdvSceneSwitcher.sceneItemSelection.all"), + QVariant::fromValue(ConflictIndex(NameClashMode::ALL))); + break; + default: + break; } + for (int i = 1; i <= sceneItemCount; ++i) { - _nameConflictIndex->addItem(QString::number(i) + "."); - } + _nameConflictIndex->addItem( + QString::number(i) + ".", + QVariant::fromValue(ConflictIndex(i - 1))); + }; + adjustSize(); updateGeometry(); } @@ -1061,49 +1119,55 @@ void SceneItemSelectionWidget::SetWidgetVisibility() case SceneItemSelection::Type::SOURCE_NAME: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.sourceName.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.sourceName.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::VARIABLE_NAME: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.sourceVariable.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_NAME_PATTERN: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.sourceNamePattern.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_GROUP: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.sourceGroup.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::SOURCE_TYPE: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.sourceType.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.sourceType.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::INDEX: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.index.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.index.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::INDEX_RANGE: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.indexRange.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.indexRange.layout"), _controlsLayout, widgetMap, false); break; case SceneItemSelection::Type::ALL: PlaceWidgets( obs_module_text( - "AdvSceneSwitcher.sceneItemSelection.type.all.entry"), + "AdvSceneSwitcher.sceneItemSelection.type.all.layout"), + _controlsLayout, widgetMap, false); + break; + case SceneItemSelection::Type::ANY: + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.sceneItemSelection.type.any.layout"), _controlsLayout, widgetMap, false); break; default: @@ -1149,6 +1213,10 @@ void SceneItemSelectionWidget::SetWidgetVisibility() _indexEnd->setVisible(_currentSelection._type == SceneItemSelection::Type::INDEX_RANGE); + if (_nameClashMode == NameClashMode::HIDE) { + _nameConflictIndex->hide(); + } + adjustSize(); updateGeometry(); } @@ -1164,7 +1232,6 @@ SceneItemTypeSelection::SceneItemTypeSelection( setWindowModality(Qt::WindowModality::WindowModal); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - populateMessageTypeSelection(_typeSelection); _typeSelection->setCurrentIndex( _typeSelection->findData(static_cast(type))); @@ -1178,10 +1245,13 @@ SceneItemTypeSelection::SceneItemTypeSelection( setLayout(layout); } -bool SceneItemTypeSelection::AskForSettings(QWidget *parent, - SceneItemSelection::Type &type) +bool SceneItemTypeSelection::AskForSettings( + QWidget *parent, SceneItemSelection::Type &type, + const std::vector &allowedTypes) { SceneItemTypeSelection dialog(parent, type); + dialog._typeSelection->clear(); + populateMessageTypeSelection(dialog._typeSelection, allowedTypes); dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle")); if (dialog.exec() != DialogCode::Accepted) { return false; diff --git a/plugins/base/utils/scene-item-selection.hpp b/plugins/base/utils/scene-item-selection.hpp index cf9d4611..bfa98952 100644 --- a/plugins/base/utils/scene-item-selection.hpp +++ b/plugins/base/utils/scene-item-selection.hpp @@ -29,6 +29,7 @@ public: INDEX = 30, INDEX_RANGE = 40, ALL = 50, + ANY = 60, }; // Name conflicts can happen if multiple instances of a given source are @@ -43,14 +44,16 @@ public: INDIVIDUAL, }; + void SetType(Type t) { _type = t; } Type GetType() const { return _type; } NameConflictSelection GetIndexType() const; std::vector GetSceneItems(const SceneSelection &) const; + bool IsSelectionOfTypeAny() const; std::string ToString(bool resolve = false) const; // TODO: Remove in future version // - // Only exists to enable backwards compatabilty with older versions of + // Only exists to enable backwards compatibility with older versions of // scene item visibility action void SetSourceTypeSelection(const char *); @@ -69,7 +72,7 @@ private: std::vector GetAllSceneItems(const SceneSelection &) const; - void ReduceBadedOnIndexSelection(std::vector &) const; + void ReduceBasedOnIndexSelection(std::vector &) const; Type _type = Type::SOURCE_NAME; @@ -90,13 +93,24 @@ class SceneItemSelectionWidget : public QWidget { Q_OBJECT public: - enum class Placeholder { ALL, ANY }; - SceneItemSelectionWidget(QWidget *parent, bool addPlaceholder = true, - Placeholder placeholderType = Placeholder::ALL); + enum class NameClashMode { NONE, ALL, ANY, ANY_AND_ALL, HIDE }; + explicit SceneItemSelectionWidget( + QWidget *parent, + const std::vector &selections = + { + SceneItemSelection::Type::SOURCE_NAME, + SceneItemSelection::Type::VARIABLE_NAME, + SceneItemSelection::Type::SOURCE_NAME_PATTERN, + SceneItemSelection::Type::SOURCE_GROUP, + SceneItemSelection::Type::SOURCE_TYPE, + SceneItemSelection::Type::INDEX, + SceneItemSelection::Type::INDEX_RANGE, + SceneItemSelection::Type::ALL, + SceneItemSelection::Type::ANY, + }, + NameClashMode mode = NameClashMode::ANY_AND_ALL); void SetSceneItem(const SceneItemSelection &); void SetScene(const SceneSelection &); - void ShowPlaceholder(bool); - void SetPlaceholderType(Placeholder t, bool resetSelection = true); protected: void showEvent(QShowEvent *event) override; @@ -144,10 +158,8 @@ private: SceneSelection _scene; SceneItemSelection _currentSelection; - bool _hasPlaceholderEntry = false; - Placeholder _placeholder = Placeholder::ALL; - - bool _showTypeSelection = false; + NameClashMode _nameClashMode = NameClashMode::ALL; + const std::vector _selectionTypes; }; class SceneItemTypeSelection : public QDialog { @@ -156,8 +168,9 @@ class SceneItemTypeSelection : public QDialog { public: SceneItemTypeSelection(QWidget *parent, const SceneItemSelection::Type &type); - static bool AskForSettings(QWidget *parent, - SceneItemSelection::Type &type); + static bool AskForSettings( + QWidget *parent, SceneItemSelection::Type &type, + const std::vector &allowedTypes); private: QComboBox *_typeSelection;