Skip to content

Commit

Permalink
UI: Add transition management APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
Elgato-AStory committed Dec 14, 2023
1 parent 4b28631 commit 84521c3
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 40 deletions.
14 changes: 14 additions & 0 deletions UI/api-interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ struct OBSStudioAPI : obs_frontend_callbacks {
}
}

void obs_frontend_add_transition(obs_source_t *transition) override
{
QMetaObject::invokeMethod(main, "AddTransitionInstance",
Q_ARG(OBSSource,
OBSSource(transition)));
}

void obs_frontend_remove_transition(obs_source_t *transition) override
{
QMetaObject::invokeMethod(main, "RemoveTransitionInstance",
Q_ARG(OBSSource,
OBSSource(transition)));
}

void obs_frontend_get_transitions(
struct obs_frontend_source_list *sources) override
{
Expand Down
12 changes: 12 additions & 0 deletions UI/obs-frontend-api/obs-frontend-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ void obs_frontend_set_current_scene(obs_source_t *scene)
c->obs_frontend_set_current_scene(scene);
}

void obs_frontend_add_transition(obs_source_t *transition)
{
if (callbacks_valid())
c->obs_frontend_add_transition(transition);
}

void obs_frontend_remove_transition(obs_source_t *transition)
{
if (callbacks_valid())
c->obs_frontend_remove_transition(transition);
}

void obs_frontend_get_transitions(struct obs_frontend_source_list *sources)
{
if (callbacks_valid())
Expand Down
2 changes: 2 additions & 0 deletions UI/obs-frontend-api/obs-frontend-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ EXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_scene(void);
EXPORT void obs_frontend_set_current_scene(obs_source_t *scene);

EXPORT void obs_frontend_add_transition(obs_source_t *transition);
EXPORT void obs_frontend_remove_transition(obs_source_t *transition);
EXPORT void
obs_frontend_get_transitions(struct obs_frontend_source_list *sources);
EXPORT obs_source_t *obs_frontend_get_current_transition(void);
Expand Down
3 changes: 3 additions & 0 deletions UI/obs-frontend-api/obs-frontend-internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ struct obs_frontend_callbacks {
virtual obs_source_t *obs_frontend_get_current_scene(void) = 0;
virtual void obs_frontend_set_current_scene(obs_source_t *scene) = 0;

virtual void obs_frontend_add_transition(obs_source_t *transition) = 0;
virtual void
obs_frontend_remove_transition(obs_source_t *transition) = 0;
virtual void obs_frontend_get_transitions(
struct obs_frontend_source_list *sources) = 0;
virtual obs_source_t *obs_frontend_get_current_transition(void) = 0;
Expand Down
176 changes: 136 additions & 40 deletions UI/window-basic-main-transitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,50 @@ void OBSBasic::RemoveQuickTransitionHotkey(QuickTransition *qt)
obs_hotkey_unregister(qt->hotkey);
}

void OBSBasic::InitTransition(obs_source_t *transition)
static void initTransitionOnStop(void *data, calldata_t *)
{
auto onTransitionStop = [](void *data, calldata_t *) {
OBSBasic *window = (OBSBasic *)data;
QMetaObject::invokeMethod(window, "TransitionStopped",
Qt::QueuedConnection);
};
OBSBasic *window = (OBSBasic *)data;
QMetaObject::invokeMethod(window, "TransitionStopped",
Qt::QueuedConnection);
}

auto onTransitionFullStop = [](void *data, calldata_t *) {
OBSBasic *window = (OBSBasic *)data;
QMetaObject::invokeMethod(window, "TransitionFullyStopped",
Qt::QueuedConnection);
};
static void initTransitionOnFullStop(void *data, calldata_t *)
{
OBSBasic *window = (OBSBasic *)data;
QMetaObject::invokeMethod(window, "TransitionFullyStopped",
Qt::QueuedConnection);
}

static void initTransitionOnRename(void *data, calldata_t *calldata)
{
OBSBasic *window = (OBSBasic *)data;
OBSSource source((obs_source_t *)calldata_ptr(calldata, "source"));

QMetaObject::invokeMethod(window, "TransitionWasRenamed",
Qt::QueuedConnection,
Q_ARG(OBSSource, source));
}

void OBSBasic::InitTransition(obs_source_t *transition)
{
signal_handler_t *handler = obs_source_get_signal_handler(transition);
signal_handler_connect(handler, "transition_video_stop",
onTransitionStop, this);
signal_handler_connect(handler, "transition_stop", onTransitionFullStop,
this);
initTransitionOnStop, this);
signal_handler_connect(handler, "transition_stop",
initTransitionOnFullStop, this);
signal_handler_connect(handler, "rename", initTransitionOnRename, this);
}

void OBSBasic::DeinitTransition(obs_source_t *transition)
{
signal_handler_t *handler = obs_source_get_signal_handler(transition);

signal_handler_disconnect(handler, "transition_video_stop",
initTransitionOnStop, this);
signal_handler_disconnect(handler, "transition_stop",
initTransitionOnFullStop, this);
signal_handler_disconnect(handler, "rename", initTransitionOnRename,
this);
}

static inline OBSSource GetTransitionComboItem(QComboBox *combo, int idx)
Expand Down Expand Up @@ -256,6 +281,38 @@ void OBSBasic::TransitionToScene(OBSScene scene, bool force)
TransitionToScene(source, force);
}

void OBSBasic::TransitionWasRenamed(OBSSource transition)
{
int idx = ui->transitions->findData(QVariant::fromValue(transition));
if (idx == -1) {
return;
}

string name = obs_source_get_name(transition);

obs_source_t *source = FindTransition(name.c_str());
if (source != transition) {
int copy = 1;
string updatedName = name;
while (source) {
updatedName = name + " " + to_string(++copy);
source = FindTransition(updatedName.c_str());
}
if (copy > 1) {
name = std::move(updatedName);
obs_source_set_name(transition, name.c_str());
}
}

ui->transitions->setItemText(idx, QT_UTF8(name.c_str()));

if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);

ClearQuickTransitionWidgets();
RefreshQuickTransitions();
}

void OBSBasic::TransitionStopped()
{
if (swapScenesMode) {
Expand Down Expand Up @@ -427,6 +484,69 @@ static inline void SetComboTransition(QComboBox *combo, obs_source_t *tr)
}
}

void OBSBasic::AddTransitionInstance(OBSSource transition)
{
int idx = ui->transitions->findData(QVariant::fromValue(transition));
if (idx != -1)
return;

string name = obs_source_get_name(transition);

obs_source_t *source = FindTransition(name.c_str());
int copy = 1;
string updatedName = name;
while (source) {
updatedName = name + " " + to_string(++copy);
source = FindTransition(updatedName.c_str());
}
if (copy > 1) {
name = std::move(updatedName);
obs_source_set_name(transition, name.c_str());
}

InitTransition(transition);

ui->transitions->addItem(QT_UTF8(name.c_str()),
QVariant::fromValue(transition));

if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);

ClearQuickTransitionWidgets();
RefreshQuickTransitions();
}

void OBSBasic::RemoveTransitionInstance(OBSSource transition)
{
if (!transition || !obs_source_configurable(transition))
return;

int idx = ui->transitions->findData(QVariant::fromValue(transition));
if (idx == -1)
return;

DeinitTransition(transition);

for (size_t i = quickTransitions.size(); i > 0; i--) {
QuickTransition &qt = quickTransitions[i - 1];
if (qt.source == transition) {
if (qt.button)
qt.button->deleteLater();
RemoveQuickTransitionHotkey(&qt);
quickTransitions.erase(quickTransitions.begin() + i -
1);
}
}

ui->transitions->removeItem(idx);

if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);

ClearQuickTransitionWidgets();
RefreshQuickTransitions();
}

void OBSBasic::SetTransition(OBSSource transition)
{
OBSSourceAutoRelease oldTransition = obs_get_output_source(0);
Expand Down Expand Up @@ -544,32 +664,8 @@ void OBSBasic::on_transitionAdd_clicked()
void OBSBasic::on_transitionRemove_clicked()
{
OBSSource tr = GetCurrentTransition();

if (!tr || !obs_source_configurable(tr) || !QueryRemoveSource(tr))
return;

int idx = ui->transitions->findData(QVariant::fromValue<OBSSource>(tr));
if (idx == -1)
return;

for (size_t i = quickTransitions.size(); i > 0; i--) {
QuickTransition &qt = quickTransitions[i - 1];
if (qt.source == tr) {
if (qt.button)
qt.button->deleteLater();
RemoveQuickTransitionHotkey(&qt);
quickTransitions.erase(quickTransitions.begin() + i -
1);
}
}

ui->transitions->removeItem(idx);

if (api)
api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);

ClearQuickTransitionWidgets();
RefreshQuickTransitions();
if (QueryRemoveSource(tr))
RemoveTransitionInstance(tr);
}

void OBSBasic::RenameTransition(OBSSource transition)
Expand Down
4 changes: 4 additions & 0 deletions UI/window-basic-main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ class OBSBasic : public OBSMainWindow {

void InitDefaultTransitions();
void InitTransition(obs_source_t *transition);
void DeinitTransition(obs_source_t *transition);
obs_source_t *FindTransition(const char *name);
OBSSource GetCurrentTransition();
obs_data_array_t *SaveTransitions();
Expand Down Expand Up @@ -719,6 +720,8 @@ public slots:
void SaveProjectDeferred();
void SaveProject();

void AddTransitionInstance(OBSSource transition);
void RemoveTransitionInstance(OBSSource transition);
void SetTransition(OBSSource transition);
void OverrideTransition(OBSSource transition);
void TransitionToScene(OBSScene scene, bool force = false);
Expand Down Expand Up @@ -767,6 +770,7 @@ private slots:

void AddTransition(const char *id);
void RenameTransition(OBSSource transition);
void TransitionWasRenamed(OBSSource transition);
void TransitionClicked();
void TransitionStopped();
void TransitionFullyStopped();
Expand Down
18 changes: 18 additions & 0 deletions docs/sphinx/reference-frontend-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,24 @@ Functions

---------------------------------------

.. function:: void obs_frontend_add_transition(obs_source_t *transition)

:param transition: The transition to add to the frontend's transition list.
This source should be created with
:c:func:`obs_source_create_private()` to ensure behavior is
consistent with the transitions created by a user.
The transition can be safely renamed with
:c:func:`obs_source_set_name()`

---------------------------------------

.. function:: void obs_frontend_remove_transition(obs_source_t *transition)

:param transition: The transition that should be removed from the frontend's
transition list

---------------------------------------

.. function:: void obs_frontend_get_transitions(struct obs_frontend_source_list *sources)

:param sources: Pointer to a :c:type:`obs_frontend_source_list`
Expand Down

0 comments on commit 84521c3

Please sign in to comment.