SceneSwitcher/src/utils/scene-item-selection.cpp
WarmUpTill d759ded64d Use FilterComboBox instead of regular QComboBox
This required the following adjustments:

Instead of having a dedicated entry indicating the empty selection the
setPlaceholderText() mechanism is used.
Thus the locations where the 1st entry was assumed to be the empty
selection would have to be adjusted.

Additional checks for the empty string / index -1 have been added.

FindIdxInRagne() was adjusted to return -1 instead of 0 in case the
given string was not found.

Switched to index based singal instead of text based signal to be
notified about selection changes.
2023-07-15 11:05:19 +02:00

504 lines
13 KiB
C++

#include "scene-item-selection.hpp"
#include <obs-module.h>
namespace advss {
constexpr std::string_view typeSaveName = "type";
constexpr std::string_view itemSaveName = "item";
constexpr std::string_view idxSaveName = "idx";
constexpr std::string_view idxTypeSaveName = "idxType";
void SceneItemSelection::Save(obs_data_t *obj, const char *name) const
{
auto data = obs_data_create();
obs_data_set_int(data, typeSaveName.data(), static_cast<int>(_type));
obs_data_set_int(data, idxTypeSaveName.data(),
static_cast<int>(_idxType));
if (_idxType == IdxType::INDIVIDUAL) {
obs_data_set_int(data, idxSaveName.data(), _idx);
} else {
obs_data_set_int(data, idxSaveName.data(), 0);
}
if (_type == SceneItemSelection::Type::SOURCE) {
obs_data_set_string(data, itemSaveName.data(),
GetWeakSourceName(_sceneItem).c_str());
} else {
auto var = _variable.lock();
if (var) {
obs_data_set_string(data, itemSaveName.data(),
var->Name().c_str());
}
}
obs_data_set_obj(obj, name, data);
obs_data_release(data);
}
// TODO: Remove in future version
void SceneItemSelection::Load(obs_data_t *obj, const char *name,
const char *typeName, const char *idxName)
{
_type = Type::SOURCE;
_idxType = static_cast<IdxType>(obs_data_get_int(obj, typeName));
_idx = obs_data_get_int(obj, idxName);
auto sceneItemName = obs_data_get_string(obj, name);
_sceneItem = GetWeakSourceByName(sceneItemName);
}
void SceneItemSelection::Load(obs_data_t *obj, const char *name)
{
// TODO: Remove in future version
if (!obs_data_has_user_value(obj, name)) {
Load(obj, "sceneItem", "sceneItemTarget", "sceneItemIdx");
return;
}
auto data = obs_data_get_obj(obj, name);
_type = static_cast<Type>(obs_data_get_int(data, typeSaveName.data()));
_idxType = static_cast<IdxType>(
obs_data_get_int(data, idxTypeSaveName.data()));
_idx = obs_data_get_int(data, idxSaveName.data());
const auto itemName = obs_data_get_string(data, itemSaveName.data());
switch (_type) {
case SceneItemSelection::Type::SOURCE:
_sceneItem = GetWeakSourceByName(itemName);
break;
case SceneItemSelection::Type::VARIABLE:
_variable = GetWeakVariableByName(itemName);
break;
default:
break;
}
obs_data_release(data);
}
struct ItemInfo {
std::string name;
std::vector<obs_sceneitem_t *> items = {};
};
static bool getSceneItems(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
ItemInfo *moveInfo = reinterpret_cast<ItemInfo *>(ptr);
auto sourceName = obs_source_get_name(obs_sceneitem_get_source(item));
if (moveInfo->name == sourceName) {
obs_sceneitem_addref(item);
moveInfo->items.push_back(item);
}
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, getSceneItems, ptr);
}
return true;
}
static std::vector<obs_scene_item *>
getSceneItemsWithName(obs_scene_t *scene, const std::string &name)
{
ItemInfo itemInfo = {name};
obs_scene_enum_items(scene, getSceneItems, &itemInfo);
return itemInfo.items;
}
struct ItemCountData {
std::string name;
int count = 0;
};
static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
auto data = reinterpret_cast<ItemCountData *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, countSceneItem, ptr);
}
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
if (name == data->name) {
data->count++;
}
return true;
}
static int getCountOfSceneItemOccurance(const SceneSelection &s,
const std::string &name,
bool enumAllScenes = true)
{
ItemCountData data{name};
if (enumAllScenes && (s.GetType() != SceneSelection::Type::SCENE)) {
auto enumScenes = [](void *param, obs_source_t *source) {
if (!source) {
return true;
}
auto data = reinterpret_cast<ItemCountData *>(param);
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, countSceneItem, data);
return true;
};
obs_enum_scenes(enumScenes, &data);
} else {
auto source = obs_weak_source_get_source(s.GetScene(false));
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, countSceneItem, &data);
obs_source_release(source);
}
return data.count;
}
std::vector<obs_scene_item *>
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection) const
{
std::vector<obs_scene_item *> ret;
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
auto scene = obs_scene_from_source(s);
std::string name;
if (_type == Type::VARIABLE) {
auto var = _variable.lock();
if (!var) {
return ret;
}
name = var->Value();
} else {
name = GetWeakSourceName(_sceneItem);
}
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
auto items = getSceneItemsWithName(scene, name);
obs_source_release(s);
if (_idxType == SceneItemSelection::IdxType::ALL ||
_idxType == SceneItemSelection::IdxType::ANY) {
ret = items;
} else {
// Index order starts at the bottom and increases to the top
// As this might be confusing reverse that order internally
int idx = count - 1 - _idx;
if (idx >= 0 && idx < (int)items.size()) {
obs_sceneitem_addref(items[idx]);
ret.emplace_back(items[idx]);
}
for (auto item : items) {
obs_sceneitem_release(item);
}
}
return ret;
}
std::string SceneItemSelection::ToString(bool resolve) const
{
if (_type == Type::VARIABLE) {
auto var = _variable.lock();
if (!var) {
return "";
}
if (resolve) {
return var->Name() + "[" + var->Value() + "]";
}
return var->Name();
}
return GetWeakSourceName(_sceneItem);
}
static bool enumSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
QStringList *names = reinterpret_cast<QStringList *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, enumSceneItem, ptr);
}
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
names->append(name);
return true;
}
static void populateSceneItemSelection(QComboBox *list)
{
QStringList names;
obs_scene_enum_items(nullptr, enumSceneItem, &names);
names.removeDuplicates();
names.sort();
list->addItems(names);
AddSelectionEntry(list, obs_module_text("AdvSceneSwitcher.selectItem"));
list->setCurrentIndex(0);
}
QStringList GetSceneItemsList(SceneSelection &s)
{
QStringList names;
if (s.GetType() != SceneSelection::Type::SCENE) {
auto enumScenes = [](void *param, obs_source_t *source) {
if (!source) {
return true;
}
QStringList *names =
reinterpret_cast<QStringList *>(param);
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, enumSceneItem, names);
return true;
};
obs_enum_scenes(enumScenes, &names);
} else {
auto source = obs_weak_source_get_source(s.GetScene(false));
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, enumSceneItem, &names);
obs_source_release(source);
}
names.removeDuplicates();
names.sort();
return names;
}
void SceneItemSelectionWidget::Reset()
{
auto previousSel = _currentSelection;
PopulateItemSelection();
SetSceneItem(previousSel);
}
void SceneItemSelectionWidget::PopulateItemSelection()
{
_sceneItems->clear();
const QStringList variables = GetVariablesNameList();
AddSelectionGroup(_sceneItems, variables);
_variablesEndIdx = _sceneItems->count();
const QStringList sceneItmes = GetSceneItemsList(_scene);
AddSelectionGroup(_sceneItems, sceneItmes, false);
_itemsEndIdx = _sceneItems->count();
_sceneItems->setCurrentIndex(-1);
}
SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
bool showAll,
Placeholder type)
: QWidget(parent), _hasPlaceholderEntry(showAll), _placeholder(type)
{
_sceneItems = new FilterComboBox(
this, obs_module_text("AdvSceneSwitcher.selectItem"));
_idx = new QComboBox();
_sceneItems->setSizeAdjustPolicy(QComboBox::AdjustToContents);
_idx->setSizeAdjustPolicy(QComboBox::AdjustToContents);
populateSceneItemSelection(_sceneItems);
QWidget::connect(_sceneItems, SIGNAL(currentIndexChanged(int)), this,
SLOT(SelectionChanged(int)));
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
SLOT(IdxChanged(int)));
// Variables
QWidget::connect(window(), SIGNAL(VariableAdded(const QString &)), this,
SLOT(ItemAdd(const QString &)));
QWidget::connect(window(), SIGNAL(VariableRemoved(const QString &)),
this, SLOT(ItemRemove(const QString &)));
QWidget::connect(
window(),
SIGNAL(VariableRenamed(const QString &, const QString &)), this,
SLOT(ItemRename(const QString &, const QString &)));
auto layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_idx);
layout->addWidget(_sceneItems);
setLayout(layout);
_idx->hide();
}
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
{
int itemIdx = -1;
switch (item._type) {
case SceneItemSelection::Type::SOURCE: {
int idx = item._idx;
if (_hasPlaceholderEntry) {
idx += 1;
}
_idx->setCurrentIndex(idx);
itemIdx = FindIdxInRagne(_sceneItems, _variablesEndIdx,
_itemsEndIdx,
GetWeakSourceName(item._sceneItem));
_sceneItems->setCurrentIndex(itemIdx);
break;
}
case SceneItemSelection::Type::VARIABLE: {
auto var = item._variable.lock();
if (!var) {
break;
}
itemIdx = FindIdxInRagne(_sceneItems, _selectIdx,
_variablesEndIdx, var->Name());
_sceneItems->setCurrentIndex(itemIdx);
break;
}
}
switch (item._idxType) {
case SceneItemSelection::IdxType::ALL:
case SceneItemSelection::IdxType::ANY:
_placeholder = Placeholder::ALL;
_idx->setCurrentIndex(0);
break;
case SceneItemSelection::IdxType::INDIVIDUAL:
int idx = item._idx;
if (_hasPlaceholderEntry) {
idx += 1;
}
_idx->setCurrentIndex(idx);
break;
}
_currentSelection = item;
}
void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
{
_scene = s;
_sceneItems->clear();
_idx->hide();
PopulateItemSelection();
}
void SceneItemSelectionWidget::ShowPlaceholder(bool value)
{
_hasPlaceholderEntry = value;
}
void SceneItemSelectionWidget::SetPlaceholderType(Placeholder t,
bool resetSelection)
{
_placeholder = t;
if (resetSelection) {
_sceneItems->setCurrentIndex(-1);
} else {
auto count = _idx->count() - 1;
const QSignalBlocker b(_idx);
SetupIdxSelection(count > 0 ? count : 1);
}
}
void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s)
{
SetScene(s);
adjustSize();
}
SceneItemSelection SceneItemSelectionWidget::CurrentSelection()
{
SceneItemSelection s;
const int idx = _sceneItems->currentIndex();
const auto name = _sceneItems->currentText();
int sceneItemCount =
getCountOfSceneItemOccurance(_scene, name.toStdString());
if (sceneItemCount > 1) {
_idx->show();
SetupIdxSelection(sceneItemCount);
} else {
_idx->hide();
}
if (_hasPlaceholderEntry) {
switch (_placeholder) {
case SceneItemSelectionWidget::Placeholder::ALL:
s._idxType = SceneItemSelection::IdxType::ALL;
break;
case SceneItemSelectionWidget::Placeholder::ANY:
s._idxType = SceneItemSelection::IdxType::ANY;
break;
}
}
if (idx == -1 || name.isEmpty()) {
return s;
}
if (idx < _variablesEndIdx) {
s._type = SceneItemSelection::Type::VARIABLE;
s._variable = GetWeakVariableByQString(name);
} else if (idx < _itemsEndIdx) {
s._type = SceneItemSelection::Type::SOURCE;
s._sceneItem = GetWeakSourceByQString(name);
}
return s;
}
void SceneItemSelectionWidget::SelectionChanged(int)
{
_currentSelection = CurrentSelection();
emit SceneItemChanged(_currentSelection);
}
void SceneItemSelectionWidget::IdxChanged(int idx)
{
if (idx < 0) {
return;
}
_currentSelection._idx = idx;
if (_hasPlaceholderEntry && idx == 0) {
switch (_placeholder) {
case SceneItemSelectionWidget::Placeholder::ALL:
_currentSelection._idxType =
SceneItemSelection::IdxType::ALL;
break;
case SceneItemSelectionWidget::Placeholder::ANY:
_currentSelection._idxType =
SceneItemSelection::IdxType::ANY;
break;
}
}
if (_hasPlaceholderEntry && idx > 0) {
_currentSelection._idx -= 1;
_currentSelection._idxType =
SceneItemSelection::IdxType::INDIVIDUAL;
}
emit SceneItemChanged(_currentSelection);
}
void SceneItemSelectionWidget::ItemAdd(const QString &)
{
blockSignals(true);
Reset();
blockSignals(false);
}
void SceneItemSelectionWidget::ItemRemove(const QString &)
{
Reset();
}
void SceneItemSelectionWidget::ItemRename(const QString &, const QString &)
{
blockSignals(true);
Reset();
blockSignals(false);
}
void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount)
{
_idx->clear();
if (_hasPlaceholderEntry) {
if (_placeholder == Placeholder::ALL) {
_idx->addItem(obs_module_text(
"AdvSceneSwitcher.sceneItemSelection.all"));
} else {
_idx->addItem(obs_module_text(
"AdvSceneSwitcher.sceneItemSelection.any"));
}
}
for (int i = 1; i <= sceneItemCount; ++i) {
_idx->addItem(QString::number(i) + ".");
}
adjustSize();
}
} // namespace advss