Add more transform actions

* Reset
* Rotation
* Flip (horizontal / vertical)
* Fit to screen
* Stretch to screen
* Center (horizontal / vertical / to screen)
This commit is contained in:
WarmUpTill
2023-07-20 21:39:19 +02:00
committed by WarmUpTill
parent bdff84c605
commit f92ed3139d
7 changed files with 361 additions and 32 deletions

View File

@@ -516,7 +516,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="An Position verschieben"
AdvSceneSwitcher.action.sceneOrder.entry="Auf{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Szenenelement transformieren"
AdvSceneSwitcher.action.sceneTransform.getTransform="Transformation erhalten"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}transformieren{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="Auf{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Datei"
AdvSceneSwitcher.action.file.type.write="Schreiben"
AdvSceneSwitcher.action.file.type.append="Anhängen"

View File

@@ -617,8 +617,18 @@ AdvSceneSwitcher.action.sceneOrder.type.moveBottom="Move to bottom"
AdvSceneSwitcher.action.sceneOrder.type.movePosition="Move to position"
AdvSceneSwitcher.action.sceneOrder.entry="On{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Scene item transform"
AdvSceneSwitcher.action.sceneTransform.type.manual="Transform"
AdvSceneSwitcher.action.sceneTransform.type.reset="Reset transform"
AdvSceneSwitcher.action.sceneTransform.type.rotate="Rotate by"
AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal="Flip horizontally"
AdvSceneSwitcher.action.sceneTransform.type.flipVertical="Flip vertically"
AdvSceneSwitcher.action.sceneTransform.type.fitToScreen="Fit to screen"
AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen="Stretch to screen"
AdvSceneSwitcher.action.sceneTransform.type.centerToScreen="Center to screen"
AdvSceneSwitcher.action.sceneTransform.type.centerVertically="Center vertically"
AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally="Center horizontally"
AdvSceneSwitcher.action.sceneTransform.getTransform="Get transform"
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}transform{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="On{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="File"
AdvSceneSwitcher.action.file.type.write="Write"
AdvSceneSwitcher.action.file.type.append="Append"

View File

@@ -432,7 +432,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Mover a la posición"
AdvSceneSwitcher.action.sceneOrder.entry="En{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Transformar elemento de escena"
AdvSceneSwitcher.action.sceneTransform.getTransform="Obtener transformación"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}transformar{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="En{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Archivo"
AdvSceneSwitcher.action.file.type.write="Escribir"
AdvSceneSwitcher.action.file.type.append="Agregar"

View File

@@ -360,7 +360,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="Konuma taşı"
AdvSceneSwitcher.action.sceneOrder.entry="Açık{{scenes}}{{actions}}{{sources}}{{position}}"
AdvSceneSwitcher.action.sceneTransform="Sahne öğesi dönüşümü"
AdvSceneSwitcher.action.sceneTransform.getTransform="Dönüşümü al"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}transform{{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="Açık{{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="Dosya"
AdvSceneSwitcher.action.file.type.write="Yaz"
AdvSceneSwitcher.action.file.type.append="Ekle"

View File

@@ -569,7 +569,7 @@ AdvSceneSwitcher.action.sceneOrder.type.movePosition="移动到数值位置"
AdvSceneSwitcher.action.sceneOrder.entry="在 {{scenes}} {{actions}} {{sources}} {{position}}"
AdvSceneSwitcher.action.sceneTransform="场景项目转换"
AdvSceneSwitcher.action.sceneTransform.getTransform="获得转换"
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}} 上转换 {{sources}}"
AdvSceneSwitcher.action.sceneTransform.entry="在 {{scenes}}{{action}}{{rotation}}{{sources}}"
AdvSceneSwitcher.action.file="文件"
AdvSceneSwitcher.action.file.type.write="写入"
AdvSceneSwitcher.action.file.type.append="追加写入"

View File

@@ -1,6 +1,8 @@
#include "macro-action-scene-transform.hpp"
#include "utility.hpp"
#include <graphics/matrix4.h>
namespace advss {
const std::string MacroActionSceneTransform::id = "scene_transform";
@@ -11,17 +13,243 @@ bool MacroActionSceneTransform::_registered = MacroActionFactory::Register(
MacroActionSceneTransformEdit::Create,
"AdvSceneSwitcher.action.sceneTransform"});
const static std::map<MacroActionSceneTransform::Action, std::string> actions = {
{MacroActionSceneTransform::Action::MANUAL_TRANSFORM,
"AdvSceneSwitcher.action.sceneTransform.type.manual"},
{MacroActionSceneTransform::Action::RESET,
"AdvSceneSwitcher.action.sceneTransform.type.reset"},
{MacroActionSceneTransform::Action::ROTATE,
"AdvSceneSwitcher.action.sceneTransform.type.rotate"},
{MacroActionSceneTransform::Action::FLIP_HORIZONTAL,
"AdvSceneSwitcher.action.sceneTransform.type.flipHorizontal"},
{MacroActionSceneTransform::Action::FLIP_VERTICAL,
"AdvSceneSwitcher.action.sceneTransform.type.flipVertical"},
{MacroActionSceneTransform::Action::FIT_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.fitToScreen"},
{MacroActionSceneTransform::Action::STRETCH_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.stretchToScreen"},
{MacroActionSceneTransform::Action::CENTER_TO_SCREEN,
"AdvSceneSwitcher.action.sceneTransform.type.centerToScreen"},
{MacroActionSceneTransform::Action::CENTER_VERTICALLY,
"AdvSceneSwitcher.action.sceneTransform.type.centerVertically"},
{MacroActionSceneTransform::Action::CENTER_HORIZONTALLY,
"AdvSceneSwitcher.action.sceneTransform.type.centerHorizontally"},
};
/* --------------- Helpers based on OBS window-basic-main.cpp --------------- */
static void getItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)
{
matrix4 boxTransform;
obs_sceneitem_get_box_transform(item, &boxTransform);
vec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);
vec3_set(&br, -M_INFINITE, -M_INFINITE, 0.0f);
auto GetMinPos = [&](float x, float y) {
vec3 pos;
vec3_set(&pos, x, y, 0.0f);
vec3_transform(&pos, &pos, &boxTransform);
vec3_min(&tl, &tl, &pos);
vec3_max(&br, &br, &pos);
};
GetMinPos(0.0f, 0.0f);
GetMinPos(1.0f, 0.0f);
GetMinPos(0.0f, 1.0f);
GetMinPos(1.0f, 1.0f);
}
static vec3 getItemTL(obs_sceneitem_t *item)
{
vec3 tl, br;
getItemBox(item, tl, br);
return tl;
}
static void setItemTL(obs_sceneitem_t *item, const vec3 &tl)
{
vec3 newTL;
vec2 pos;
obs_sceneitem_get_pos(item, &pos);
newTL = getItemTL(item);
pos.x += tl.x - newTL.x;
pos.y += tl.y - newTL.y;
obs_sceneitem_set_pos(item, &pos);
}
static void reset(obs_sceneitem_t *item)
{
obs_transform_info info;
vec2_set(&info.pos, 0.0f, 0.0f);
vec2_set(&info.scale, 1.0f, 1.0f);
info.rot = 0.0f;
info.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
info.bounds_type = OBS_BOUNDS_NONE;
info.bounds_alignment = OBS_ALIGN_CENTER;
vec2_set(&info.bounds, 0.0f, 0.0f);
obs_sceneitem_set_info(item, &info);
obs_sceneitem_crop crop = {};
obs_sceneitem_set_crop(item, &crop);
}
static void rotate(obs_sceneitem_t *item, float rot)
{
vec3 tl = getItemTL(item);
rot += obs_sceneitem_get_rot(item);
if (rot >= 360.0f) {
rot -= 360.0f;
} else if (rot <= -360.0f) {
rot += 360.0f;
}
obs_sceneitem_set_rot(item, rot);
obs_sceneitem_force_update_transform(item);
setItemTL(item, tl);
};
static void flip(obs_sceneitem_t *item, const vec2 &mul)
{
vec3 tl = getItemTL(item);
vec2 scale;
obs_sceneitem_get_scale(item, &scale);
vec2_mul(&scale, &scale, &mul);
obs_sceneitem_set_scale(item, &scale);
obs_sceneitem_force_update_transform(item);
setItemTL(item, tl);
}
static void centerAlign(obs_sceneitem_t *item, obs_bounds_type boundsType)
{
obs_video_info ovi;
obs_get_video_info(&ovi);
obs_transform_info itemInfo;
vec2_set(&itemInfo.pos, 0.0f, 0.0f);
vec2_set(&itemInfo.scale, 1.0f, 1.0f);
itemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;
itemInfo.rot = 0.0f;
vec2_set(&itemInfo.bounds, float(ovi.base_width),
float(ovi.base_height));
itemInfo.bounds_type = boundsType;
itemInfo.bounds_alignment = OBS_ALIGN_CENTER;
obs_sceneitem_set_info(item, &itemInfo);
}
enum class CenterType {
SCREEN,
VERTICAL,
HORIZONTAL,
};
static void center(obs_sceneitem_t *item, const CenterType &centerType)
{
// Get center x, y coordinates of items
vec3 tl, br;
getItemBox(item, tl, br);
float left = tl.x;
float top = tl.y;
float right = br.x;
float bottom = br.y;
vec3 center;
center.x = (right + left) / 2.0f;
center.y = (top + bottom) / 2.0f;
center.z = 0.0f;
// Get coordinates of screen center
obs_video_info ovi;
obs_get_video_info(&ovi);
vec3 screenCenter;
vec3_set(&screenCenter, float(ovi.base_width), float(ovi.base_height),
0.0f);
vec3_mulf(&screenCenter, &screenCenter, 0.5f);
// Calculate difference between screen center and item center
vec3 offset;
vec3_sub(&offset, &screenCenter, &center);
// Shift item by offset
vec3_add(&tl, &tl, &offset);
vec3 itemTL = getItemTL(item);
if (centerType == CenterType::VERTICAL) {
tl.x = itemTL.x;
} else if (centerType == CenterType::HORIZONTAL) {
tl.y = itemTL.y;
}
setItemTL(item, tl);
}
/* ------------------------------------------------------------------------- */
void MacroActionSceneTransform::Transform(obs_scene_item *item)
{
if (!item) {
return;
}
switch (_action) {
case MacroActionSceneTransform::Action::RESET:
obs_sceneitem_defer_update_begin(item);
reset(item);
obs_sceneitem_defer_update_end(item);
break;
case MacroActionSceneTransform::Action::ROTATE:
rotate(item, _rotation);
break;
case MacroActionSceneTransform::Action::FLIP_HORIZONTAL: {
vec2 scale;
vec2_set(&scale, -1.0f, 1.0f);
flip(item, scale);
break;
}
case MacroActionSceneTransform::Action::FLIP_VERTICAL: {
vec2 scale;
vec2_set(&scale, 1.0f, -1.0f);
flip(item, scale);
break;
}
case MacroActionSceneTransform::Action::FIT_TO_SCREEN:
centerAlign(item, OBS_BOUNDS_SCALE_INNER);
break;
case MacroActionSceneTransform::Action::STRETCH_TO_SCREEN:
centerAlign(item, OBS_BOUNDS_STRETCH);
break;
case MacroActionSceneTransform::Action::CENTER_TO_SCREEN:
center(item, CenterType::SCREEN);
break;
case MacroActionSceneTransform::Action::CENTER_VERTICALLY:
center(item, CenterType::VERTICAL);
break;
case MacroActionSceneTransform::Action::CENTER_HORIZONTALLY:
center(item, CenterType::HORIZONTAL);
break;
case MacroActionSceneTransform::Action::MANUAL_TRANSFORM:
obs_sceneitem_defer_update_begin(item);
obs_sceneitem_set_info(item, &_info);
obs_sceneitem_set_crop(item, &_crop);
obs_sceneitem_defer_update_end(item);
break;
}
}
bool MacroActionSceneTransform::PerformAction()
{
ApplySettings(_settings); // Resolves variables
auto items = _source.GetSceneItems(_scene);
for (auto &item : items) {
obs_sceneitem_defer_update_begin(item);
obs_sceneitem_set_info(item, &_info);
obs_sceneitem_set_crop(item, &_crop);
obs_sceneitem_defer_update_end(item);
Transform(item);
obs_sceneitem_release(item);
}
return true;
@@ -30,15 +258,18 @@ bool MacroActionSceneTransform::PerformAction()
void MacroActionSceneTransform::LogAction() const
{
vblog(LOG_INFO,
"performed transform action for source \"%s\" on scene \"%s\"",
_source.ToString(true).c_str(), _scene.ToString(true).c_str());
"performed transform action %d for source \"%s\" on scene \"%s\"",
static_cast<int>(_action), _source.ToString(true).c_str(),
_scene.ToString(true).c_str());
}
bool MacroActionSceneTransform::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_scene.Save(obj);
_source.Save(obj);
_rotation.Save(obj, "rotation");
_settings.Save(obj, "settings");
return true;
}
@@ -53,8 +284,15 @@ bool MacroActionSceneTransform::Load(obs_data_t *obj)
}
MacroAction::Load(obj);
if (!obs_data_has_user_value(obj, "action")) {
_action =
Action::MANUAL_TRANSFORM; // TODO: Remove fallback in future
} else {
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
}
_scene.Load(obj);
_source.Load(obj);
_rotation.Load(obj, "rotation");
_settings.Load(obj, "settings");
// Convert old data format
@@ -113,15 +351,31 @@ void MacroActionSceneTransform::ApplySettings(const std::string &settings)
obs_data_release(data);
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[type, name] : actions) {
list->addItem(obs_module_text(name.c_str()),
static_cast<int>(type));
}
}
MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QWidget *parent, std::shared_ptr<MacroActionSceneTransform> entryData)
: QWidget(parent)
: QWidget(parent),
_scenes(new SceneSelectionWidget(window(), true, false, false, true)),
_sources(new SceneItemSelectionWidget(parent)),
_action(new QComboBox()),
_rotation(new VariableDoubleSpinBox()),
_getSettings(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"))),
_settings(new VariableTextEdit(this)),
_buttonLayout(new QHBoxLayout())
{
_scenes = new SceneSelectionWidget(window(), true, false, false, true);
_sources = new SceneItemSelectionWidget(parent);
_getSettings = new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sceneTransform.getTransform"));
_settings = new VariableTextEdit(this);
_rotation->setMinimum(-360);
_rotation->setMaximum(360);
_rotation->setSuffix("°");
populateActionSelection(_action);
QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)),
this, SLOT(SceneChanged(const SceneSelection &)));
@@ -130,6 +384,12 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QWidget::connect(_sources,
SIGNAL(SceneItemChanged(const SceneItemSelection &)),
this, SLOT(SourceChanged(const SceneItemSelection &)));
QWidget::connect(_action, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(
_rotation,
SIGNAL(NumberVariableChanged(const NumberVariable<double> &)),
this, SLOT(RotationChanged(const NumberVariable<double> &)));
QWidget::connect(_getSettings, SIGNAL(clicked()), this,
SLOT(GetSettingsClicked()));
QWidget::connect(_settings, SIGNAL(textChanged()), this,
@@ -138,23 +398,21 @@ MacroActionSceneTransformEdit::MacroActionSceneTransformEdit(
QHBoxLayout *entryLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{scenes}}", _scenes},
{"{{sources}}", _sources},
{"{{settings}}", _settings},
{"{{getSettings}}", _getSettings},
{"{{scenes}}", _scenes}, {"{{rotation}}", _rotation},
{"{{sources}}", _sources}, {"{{action}}", _action},
{"{{settings}}", _settings}, {"{{getSettings}}", _getSettings},
};
PlaceWidgets(
obs_module_text("AdvSceneSwitcher.action.sceneTransform.entry"),
entryLayout, widgetPlaceholders);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(_getSettings);
buttonLayout->addStretch();
_buttonLayout->addWidget(_getSettings);
_buttonLayout->addStretch();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(entryLayout);
mainLayout->addWidget(_settings);
mainLayout->addLayout(buttonLayout);
mainLayout->addLayout(_buttonLayout);
setLayout(mainLayout);
_entryData = entryData;
@@ -170,10 +428,11 @@ void MacroActionSceneTransformEdit::UpdateEntryData()
_scenes->SetScene(_entryData->_scene);
_sources->SetSceneItem(_entryData->_source);
_action->setCurrentIndex(
_action->findData(static_cast<int>(_entryData->_action)));
_rotation->SetValue(_entryData->_rotation);
_settings->setPlainText(_entryData->_settings);
adjustSize();
updateGeometry();
SetWidgetVisibility();
}
void MacroActionSceneTransformEdit::SceneChanged(const SceneSelection &s)
@@ -198,6 +457,28 @@ void MacroActionSceneTransformEdit::SourceChanged(const SceneItemSelection &item
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionSceneTransformEdit::ActionChanged(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionSceneTransform::Action>(
_action->itemData(idx).toInt());
SetWidgetVisibility();
}
void MacroActionSceneTransformEdit::RotationChanged(const DoubleVariable &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_rotation = value;
}
void MacroActionSceneTransformEdit::GetSettingsClicked()
{
if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) {
@@ -229,4 +510,19 @@ void MacroActionSceneTransformEdit::SettingsChanged()
updateGeometry();
}
void MacroActionSceneTransformEdit::SetWidgetVisibility()
{
SetLayoutVisible(
_buttonLayout,
_entryData->_action ==
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
_settings->setVisible(
_entryData->_action ==
MacroActionSceneTransform::Action::MANUAL_TRANSFORM);
_rotation->setVisible(_entryData->_action ==
MacroActionSceneTransform::Action::ROTATE);
adjustSize();
updateGeometry();
}
} // namespace advss

View File

@@ -3,6 +3,7 @@
#include "scene-selection.hpp"
#include "scene-item-selection.hpp"
#include "variable-text-edit.hpp"
#include "variable-spinbox.hpp"
#include <QSpinBox>
@@ -22,11 +23,27 @@ public:
return std::make_shared<MacroActionSceneTransform>(m);
}
enum class Action {
RESET,
ROTATE,
FLIP_HORIZONTAL,
FLIP_VERTICAL,
FIT_TO_SCREEN,
STRETCH_TO_SCREEN,
CENTER_TO_SCREEN,
CENTER_VERTICALLY,
CENTER_HORIZONTALLY,
MANUAL_TRANSFORM = 100,
};
Action _action = Action::RESET;
SceneSelection _scene;
SceneItemSelection _source;
StringVariable _settings = "";
DoubleVariable _rotation = 90.0;
private:
void Transform(obs_scene_item *);
void ApplySettings(const std::string &);
std::string ConvertSettings();
@@ -57,19 +74,25 @@ public:
private slots:
void SceneChanged(const SceneSelection &);
void SourceChanged(const SceneItemSelection &);
void ActionChanged(int);
void RotationChanged(const NumberVariable<double> &value);
void GetSettingsClicked();
void SettingsChanged();
signals:
void HeaderInfoChanged(const QString &);
protected:
private:
void SetWidgetVisibility();
SceneSelectionWidget *_scenes;
SceneItemSelectionWidget *_sources;
QComboBox *_action;
VariableDoubleSpinBox *_rotation;
QPushButton *_getSettings;
VariableTextEdit *_settings;
std::shared_ptr<MacroActionSceneTransform> _entryData;
QHBoxLayout *_buttonLayout;
private:
std::shared_ptr<MacroActionSceneTransform> _entryData;
bool _loading = true;
};