diff --git a/src/requesthandler/RequestHandler_SceneItems.cpp b/src/requesthandler/RequestHandler_SceneItems.cpp index 78e72f59..e4a9be84 100644 --- a/src/requesthandler/RequestHandler_SceneItems.cpp +++ b/src/requesthandler/RequestHandler_SceneItems.cpp @@ -86,8 +86,9 @@ RequestResult RequestHandler::GetGroupSceneItemList(const Request& request) * * Scenes and Groups * - * @requestField sceneName | String | Name of the scene or group to search in - * @requestField sourceName | String | Name of the source to find + * @requestField sceneName | String | Name of the scene or group to search in + * @requestField sourceName | String | Name of the source to find + * @requestField ?searchOffset | Number | Number of matches to skip during search. >= 0 means first forward. -1 means last (top) item | >= -1 | 0 * * @responseField sceneItemId | Number | Numeric ID of the scene item * @@ -108,9 +109,16 @@ RequestResult RequestHandler::GetSceneItemId(const Request& request) std::string sourceName = request.RequestData["sourceName"]; - OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName); + int offset = 0; + if (request.Contains("searchOffset")) { + if (!request.ValidateOptionalNumber("searchOffset", statusCode, comment, -1)) + return RequestResult::Error(statusCode, comment); + offset = request.RequestData["searchOffset"]; + } + + OBSSceneItemAutoRelease item = Utils::Obs::SearchHelper::GetSceneItemByName(scene, sourceName, offset); if (!item) - return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name."); + return RequestResult::Error(RequestStatus::ResourceNotFound, "No scene items were found in the specified scene by that name or offset."); json responseData; responseData["sceneItemId"] = obs_sceneitem_get_id(item); diff --git a/src/utils/Obs.h b/src/utils/Obs.h index 4eda079a..82ffa3a9 100644 --- a/src/utils/Obs.h +++ b/src/utils/Obs.h @@ -205,7 +205,7 @@ namespace Utils { namespace SearchHelper { obs_hotkey_t *GetHotkeyByName(std::string name); obs_source_t *GetSceneTransitionByName(std::string name); // Increments source ref. Use OBSSourceAutoRelease - obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name); // Increments ref. Use OBSSceneItemAutoRelease + obs_sceneitem_t *GetSceneItemByName(obs_scene_t *scene, std::string name, int offset = 0); // Increments ref. Use OBSSceneItemAutoRelease } namespace ActionHelper { diff --git a/src/utils/Obs_SearchHelper.cpp b/src/utils/Obs_SearchHelper.cpp index c39501f7..f16501d6 100644 --- a/src/utils/Obs_SearchHelper.cpp +++ b/src/utils/Obs_SearchHelper.cpp @@ -54,15 +54,42 @@ obs_source_t *Utils::Obs::SearchHelper::GetSceneTransitionByName(std::string nam return ret; } +struct SceneItemSearchData { + std::string name; + int offset; + obs_sceneitem_t *ret = nullptr; +}; + // Increments item ref. Use OBSSceneItemAutoRelease -obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name) +obs_sceneitem_t *Utils::Obs::SearchHelper::GetSceneItemByName(obs_scene_t *scene, std::string name, int offset) { if (name.empty()) return nullptr; - // Finds first matching scene item in scene, search starts at index 0 - OBSSceneItem ret = obs_scene_find_source(scene, name.c_str()); - obs_sceneitem_addref(ret); + SceneItemSearchData enumData; + enumData.name = name; + enumData.offset = offset; - return ret; + obs_scene_enum_items(scene, [](obs_scene_t*, obs_sceneitem_t* sceneItem, void* param) { + auto enumData = static_cast(param); + + OBSSource itemSource = obs_sceneitem_get_source(sceneItem); + std::string sourceName = obs_source_get_name(itemSource); + if (sourceName == enumData->name) { + if (enumData->offset > 0) { + enumData->offset--; + } else { + if (enumData->ret) // Release existing selection in the case of last match selection + obs_sceneitem_release(enumData->ret); + obs_sceneitem_addref(sceneItem); + enumData->ret = sceneItem; + if (enumData->offset == 0) // Only break if in normal selection mode (not offset == -1) + return false; + } + } + + return true; + }, &enumData); + + return enumData.ret; }