Add support for "any" selection in secene item selection

This commit is contained in:
WarmUpTill 2025-08-05 19:28:56 +02:00 committed by WarmUpTill
parent b27a11931a
commit 4cae420ade
18 changed files with 722 additions and 258 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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="いずれか"

View File

@ -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"

View File

@ -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="任何"

View File

@ -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);

View File

@ -113,7 +113,19 @@ MacroActionSceneLockEdit::MacroActionSceneLockEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneLock> 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);

View File

@ -276,8 +276,32 @@ MacroActionSceneOrderEdit::MacroActionSceneOrderEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneOrder> 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))
{

View File

@ -400,7 +400,19 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> 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(

View File

@ -128,7 +128,19 @@ MacroActionSceneVisibilityEdit::MacroActionSceneVisibilityEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneVisibility> 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);

View File

@ -237,7 +237,19 @@ MacroActionTransitionEdit::MacroActionTransitionEdit(
QWidget *parent, std::shared_ptr<MacroActionTransition> 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<std::string, QWidget *> widgetPlaceholders = {
const std::unordered_map<std::string, QWidget *> 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);

View File

@ -63,36 +63,100 @@ static std::vector<int> getSceneItemPositions(std::vector<OBSSceneItem> &items,
return positions;
}
static bool isAbove(std::vector<int> &pos1, std::vector<int> &pos2)
static bool anyAboveAny(const std::vector<int> &a, const std::vector<int> &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<int> &a, const std::vector<int> &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<int> &a, const std::vector<int> &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<int> &pos1, std::vector<int> &pos2)
static bool allAboveAll(const std::vector<int> &a, const std::vector<int> &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<int> &a, const std::vector<int> &b)
{
for (int ai : a)
for (int bi : b)
if (ai < bi)
return true;
return false;
}
static bool anyBelowAll(const std::vector<int> &a, const std::vector<int> &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<int> &a, const std::vector<int> &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<int> &a, const std::vector<int> &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<int> &)));
auto entryLayout = new QHBoxLayout();
std::unordered_map<std::string, QWidget *> 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()));
}

View File

@ -56,6 +56,22 @@ static bool doesTransformOfAnySceneItemMatch(
return ret;
}
static bool doesTransformOfAllSceneItemsMatch(
const std::vector<OBSSceneItem> &items, const std::string &jsonCompare,
const RegexConfig &regex, 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<OBSSceneItem> &items,
std::vector<std::string> &previousTransform,
@ -81,19 +97,57 @@ didTransformOfAnySceneItemChange(const std::vector<OBSSceneItem> &items,
return ret;
}
static bool
didTransformOfAllSceneItemsChange(const std::vector<OBSSceneItem> &items,
std::vector<std::string> &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<OBSSceneItem> &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<OBSSceneItem> &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<OBSSceneItem> &items)
{
if (_previousSettingValues.size() < items.size()) {
_previousSettingValues.resize(items.size());
return false;
}
if (_setting.GetID().empty()) {
return false;
}
bool ret = true;
std::vector<std::string> 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<OBSSceneItem> &items)
{
if (_setting.GetID().empty()) {
return false;
}
bool ret = true;
std::vector<std::string> 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()),

View File

@ -45,6 +45,10 @@ private:
bool CheckAllSettings(const std::vector<OBSSceneItem> &);
bool CheckSingleSetting(const std::vector<OBSSceneItem> &);
bool
AllSceneItemsTransformSettingChanged(const std::vector<OBSSceneItem> &);
bool
AllSceneItemsTransformSettingMatch(const std::vector<OBSSceneItem> &);
bool
AnySceneItemTransformSettingChanged(const std::vector<OBSSceneItem> &);
bool
AnySceneItemTransformSettingMatches(const std::vector<OBSSceneItem> &);

View File

@ -36,6 +36,15 @@ static bool areAllSceneItemsShown(const std::vector<OBSSceneItem> &items)
return ret;
}
static bool isAnySceneItemShown(const std::vector<OBSSceneItem> &items)
{
bool ret = false;
for (const auto &item : items) {
ret |= obs_sceneitem_visible(item);
}
return ret;
}
static bool areAllSceneItemsHidden(const std::vector<OBSSceneItem> &items)
{
bool ret = true;
@ -47,6 +56,15 @@ static bool areAllSceneItemsHidden(const std::vector<OBSSceneItem> &items)
return ret;
}
static bool isAnySceneItemHidden(const std::vector<OBSSceneItem> &items)
{
bool ret = false;
for (const auto &item : items) {
ret |= (!obs_sceneitem_visible(item));
}
return ret;
}
static bool
didVisibilityOfAnySceneItemsChange(const std::vector<OBSSceneItem> &items,
std::vector<bool> &previousVisibility)
@ -66,21 +84,53 @@ didVisibilityOfAnySceneItemsChange(const std::vector<OBSSceneItem> &items,
return ret;
}
static bool
didVisibilityOfAllSceneItemsChange(const std::vector<OBSSceneItem> &items,
std::vector<bool> &previousVisibility)
{
std::vector<bool> 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<int>(_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<Condition>(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<std::string, QWidget *> 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<MacroConditionSceneVisibility::Condition>(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<int>(_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);
}

View File

@ -31,7 +31,7 @@ public:
Condition _condition = Condition::SHOWN;
private:
std::vector<bool> _previousVisibilty;
std::vector<bool> _previousVisibility;
static bool _registered;
static const std::string id;

View File

@ -6,9 +6,18 @@
#include "ui-helpers.hpp"
#include <set>
#include <variant>
using NameClashMode = advss::SceneItemSelectionWidget::NameClashMode;
using ConflictIndex = std::variant<int, NameClashMode>;
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<SceneItemSelection::Type, std::string> 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<OBSSceneItem> &items) const
{
if (_nameConflictSelectionType ==
@ -368,7 +358,7 @@ std::vector<OBSSceneItem> SceneItemSelection::GetSceneItemsByName(
name = GetWeakSourceName(_source);
}
auto items = getSceneItemsWithName(scene, name);
ReduceBadedOnIndexSelection(items);
ReduceBasedOnIndexSelection(items);
return items;
}
@ -380,7 +370,7 @@ std::vector<OBSSceneItem> 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<OBSSceneItem> 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<SceneItemSelection::Type> &allowedTypes)
{
for (const auto &[type, name] : types) {
const static std::map<SceneItemSelection::Type, std::string> 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<int>(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<SceneItemSelection::Type> &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>("NameClashMode");
qRegisterMetaType<ConflictIndex>("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<ConflictIndex>()) {
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<decltype(val)>;
if constexpr (std::is_same_v<T, int>) {
setSelection(val, NameConflictSelection::INDIVIDUAL);
} else if constexpr (std::is_same_v<T, NameClashMode>) {
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<ConflictIndex>();
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<int>(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<SceneItemSelection::Type> &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;

View File

@ -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<OBSSceneItem> 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<OBSSceneItem>
GetAllSceneItems(const SceneSelection &) const;
void ReduceBadedOnIndexSelection(std::vector<OBSSceneItem> &) const;
void ReduceBasedOnIndexSelection(std::vector<OBSSceneItem> &) 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<SceneItemSelection::Type> &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<SceneItemSelection::Type> _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<SceneItemSelection::Type> &allowedTypes);
private:
QComboBox *_typeSelection;