diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f993c9f..74e70d80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -248,6 +248,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-selection.hpp src/headers/curl-helper.hpp src/headers/hotkey.hpp + src/headers/scene-item-selection.hpp src/headers/scene-selection.hpp src/headers/screenshot-helper.hpp src/headers/transition-selection.hpp @@ -346,6 +347,7 @@ set(advanced-scene-switcher_SOURCES src/macro-selection.cpp src/macro-tab.cpp src/curl-helper.cpp + src/scene-item-selection.cpp src/scene-selection.cpp src/screenshot-helper.cpp src/transition-selection.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 5eea77fd..9ce5f642 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -662,6 +662,8 @@ AdvSceneSwitcher.enterPath="--enter path--" AdvSceneSwitcher.enterText="--enter text--" AdvSceneSwitcher.invaildEntriesWillNotBeSaved="invalid entries will not be saved" AdvSceneSwitcher.selectWindowTip="Use \"OBS\" to specify OBS window\nUse \"Task Switching\"to specify ALT + TAB" +AdvSceneSwitcher.sceneItemSelection.all="All" +AdvSceneSwitcher.sceneItemSelection.any="Any" AdvSceneSwitcher.status.active="Active" AdvSceneSwitcher.status.inactive="Inactive" diff --git a/src/headers/scene-item-selection.hpp b/src/headers/scene-item-selection.hpp new file mode 100644 index 00000000..bba54b73 --- /dev/null +++ b/src/headers/scene-item-selection.hpp @@ -0,0 +1,59 @@ +#pragma once +#include +#include + +#include "scene-selection.hpp" +#include "utility.hpp" + +class SceneItemSelection { +public: + void Save(obs_data_t *obj, const char *name = "sceneItem", + const char *targetName = "sceneItemTarget", + const char *idxName = "sceneItemIdx"); + void Load(obs_data_t *obj, const char *name = "sceneItem", + const char *targetName = "sceneItemTarget", + const char *idxName = "sceneItemIdx"); + + enum class Target { ALL, ANY, INDIVIDUAL }; + Target GetType() { return _target; } + std::vector GetSceneItems(SceneSelection &s); + std::string ToString(); + +private: + OBSWeakSource _sceneItem; + Target _target = Target::ALL; + int _idx = 0; + friend class SceneItemSelectionWidget; +}; + +class SceneItemSelectionWidget : public QWidget { + Q_OBJECT + +public: + enum class AllSelectionType { ALL, ANY }; + SceneItemSelectionWidget( + QWidget *parent, bool allSelection = true, + AllSelectionType allType = AllSelectionType::ALL); + void SetSceneItem(const SceneItemSelection &); + void SetScene(const SceneSelection &); + void SetShowAll(bool); + void SetShowAllSelectionType(AllSelectionType t); +signals: + void SceneItemChanged(const SceneItemSelection &); + +private slots: + void SceneChanged(const SceneSelection &); + void SelectionChanged(const QString &name); + void IdxChanged(int); + +private: + void SetupIdxSelection(int); + + QComboBox *_sceneItems; + QComboBox *_idx; + + SceneSelection _scene; + OBSWeakSource _sceneItem; + bool _showAll = false; + AllSelectionType _allType = AllSelectionType::ALL; +}; diff --git a/src/scene-item-selection.cpp b/src/scene-item-selection.cpp new file mode 100644 index 00000000..3033ff66 --- /dev/null +++ b/src/scene-item-selection.cpp @@ -0,0 +1,240 @@ +#include "headers/scene-item-selection.hpp" + +#include + +void SceneItemSelection::Save(obs_data_t *obj, const char *name, + const char *targetName, const char *idxName) +{ + obs_data_set_int(obj, targetName, static_cast(_target)); + if (_target == SceneItemSelection::Target::INDIVIDUAL) { + obs_data_set_int(obj, idxName, _idx); + } else { + obs_data_set_int(obj, idxName, 0); + } + obs_data_set_string(obj, name, GetWeakSourceName(_sceneItem).c_str()); +} + +void SceneItemSelection::Load(obs_data_t *obj, const char *name, + const char *targetName, const char *idxName) +{ + _target = static_cast( + obs_data_get_int(obj, targetName)); + _idx = obs_data_get_int(obj, idxName); + auto sceneItemName = obs_data_get_string(obj, name); + _sceneItem = GetWeakSourceByName(sceneItemName); +} + +struct ItemCountData { + std::string name; + int count = 0; +}; + +static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr) +{ + auto data = reinterpret_cast(ptr); + + if (obs_sceneitem_is_group(item)) { + obs_scene_t *scene = obs_sceneitem_group_get_scene(item); + obs_scene_enum_items(scene, countSceneItem, ptr); + } + auto name = obs_source_get_name(obs_sceneitem_get_source(item)); + if (name == data->name) { + data->count++; + } + return true; +} + +int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name, + bool enumAllScenes = true) +{ + ItemCountData data{name}; + if (enumAllScenes && (s.GetType() == SceneSelectionType::CURRENT || + s.GetType() == SceneSelectionType::PREVIOUS)) { + auto enumScenes = [](void *param, obs_source_t *source) { + if (!source) { + return true; + } + auto data = reinterpret_cast(param); + auto scene = obs_scene_from_source(source); + obs_scene_enum_items(scene, 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 +SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection) +{ + auto s = obs_weak_source_get_source(sceneSelection.GetScene(false)); + auto scene = obs_scene_from_source(s); + auto name = GetWeakSourceName(_sceneItem); + int count = getCountOfSceneItemOccurance(sceneSelection, name, false); + auto items = getSceneItemsWithName(scene, name); + obs_source_release(s); + + std::vector ret; + + if (_target == SceneItemSelection::Target::ALL || + _target == SceneItemSelection::Target::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 < 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() +{ + return GetWeakSourceName(_sceneItem); +} + +SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent, + bool showAll, + AllSelectionType type) + : QWidget(parent), _showAll(showAll), _allType(type) +{ + _sceneItems = new QComboBox(); + _idx = new QComboBox(); + + _sceneItems->setSizeAdjustPolicy(QComboBox::AdjustToContents); + _idx->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + populateSceneItemSelection(_sceneItems); + + QWidget::connect(_sceneItems, + SIGNAL(currentTextChanged(const QString &)), this, + SLOT(SelectionChanged(const QString &))); + QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this, + SLOT(IdxChanged(int))); + auto layout = new QHBoxLayout; + layout->addWidget(_idx); + layout->addWidget(_sceneItems); + setLayout(layout); + _idx->hide(); +} + +void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item) +{ + _sceneItems->setCurrentText( + QString::fromStdString(GetWeakSourceName(item._sceneItem))); + if (item._target == SceneItemSelection::Target::ALL) { + _allType = AllSelectionType::ALL; + _idx->setCurrentIndex(0); + } else if (item._target == SceneItemSelection::Target::ANY) { + _allType = AllSelectionType::ANY; + _idx->setCurrentIndex(0); + } else { + int idx = item._idx; + if (_showAll) { + idx += 1; + } + _idx->setCurrentIndex(idx); + } +} + +void SceneItemSelectionWidget::SetScene(const SceneSelection &s) +{ + _scene = s; + _sceneItems->clear(); + _idx->hide(); + populateSceneItemSelection(_sceneItems, _scene); +} + +void SceneItemSelectionWidget::SetShowAll(bool value) +{ + _showAll = value; +} + +void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t) +{ + _allType = t; + _sceneItems->setCurrentIndex(0); +} + +void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s) +{ + SetScene(s); + adjustSize(); +} + +void SceneItemSelectionWidget::SelectionChanged(const QString &name) +{ + SceneItemSelection s; + _sceneItem = GetWeakSourceByQString(name); + s._sceneItem = _sceneItem; + if (_allType == AllSelectionType::ALL) { + s._target = SceneItemSelection::Target::ALL; + } else { + s._target = SceneItemSelection::Target::ANY; + } + auto stdName = name.toStdString(); + int sceneItemCount = getCountOfSceneItemOccurance(_scene, stdName); + if (sceneItemCount > 1) { + _idx->show(); + SetupIdxSelection(sceneItemCount); + } else { + _idx->hide(); + } + emit SceneItemChanged(s); +} + +void SceneItemSelectionWidget::IdxChanged(int idx) +{ + if (idx < 0) { + return; + } + + SceneItemSelection s; + s._sceneItem = _sceneItem; + if (_showAll && idx == 0) { + if (_allType == AllSelectionType::ALL) { + s._target = SceneItemSelection::Target::ALL; + } else { + s._target = SceneItemSelection::Target::ANY; + } + s._idx = 0; + } else { + s._target = SceneItemSelection::Target::INDIVIDUAL; + if (_showAll) { + idx -= 1; + } + s._idx = idx; + } + emit SceneItemChanged(s); +} + +void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount) +{ + _idx->clear(); + if (_showAll) { + if (_allType == AllSelectionType::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(); +}