obs-studio/UI/window-basic-transform.cpp
gxalpha 9eca7b7525 UI: Fix Qt 6.7 checkbox signal deprecations
qt/qtbase@3512fb1ec5 deprecated the
stateChanged signal of QCheckBoxes in favor of a new checkStateChanged
signal. The signals are the same, except that now the enum type is
passed explicitly (before the enum was passed as an argument but defined
as an int).
2024-05-28 16:29:26 -04:00

408 lines
12 KiB
C++

#include <QMessageBox>
#include "window-basic-transform.hpp"
#include "window-basic-main.hpp"
Q_DECLARE_METATYPE(OBSScene);
Q_DECLARE_METATYPE(OBSSceneItem);
static bool find_sel(obs_scene_t *, obs_sceneitem_t *item, void *param)
{
OBSSceneItem &dst = *reinterpret_cast<OBSSceneItem *>(param);
if (obs_sceneitem_selected(item)) {
dst = item;
return false;
}
if (obs_sceneitem_is_group(item)) {
obs_sceneitem_group_enum_items(item, find_sel, param);
if (!!dst) {
return false;
}
}
return true;
};
static OBSSceneItem FindASelectedItem(obs_scene_t *scene)
{
OBSSceneItem item;
obs_scene_enum_items(scene, find_sel, &item);
return item;
}
#define COMBO_CHANGED &QComboBox::currentIndexChanged
#define ISCROLL_CHANGED &QSpinBox::valueChanged
#define DSCROLL_CHANGED &QDoubleSpinBox::valueChanged
OBSBasicTransform::OBSBasicTransform(OBSSceneItem item, OBSBasic *parent)
: QDialog(parent),
ui(new Ui::OBSBasicTransform),
main(parent)
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->setupUi(this);
HookWidget(ui->positionX, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->positionY, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->rotation, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->sizeX, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->sizeY, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->align, COMBO_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->boundsType, COMBO_CHANGED,
&OBSBasicTransform::OnBoundsType);
HookWidget(ui->boundsAlign, COMBO_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->boundsWidth, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->boundsHeight, DSCROLL_CHANGED,
&OBSBasicTransform::OnControlChanged);
HookWidget(ui->cropLeft, ISCROLL_CHANGED,
&OBSBasicTransform::OnCropChanged);
HookWidget(ui->cropRight, ISCROLL_CHANGED,
&OBSBasicTransform::OnCropChanged);
HookWidget(ui->cropTop, ISCROLL_CHANGED,
&OBSBasicTransform::OnCropChanged);
HookWidget(ui->cropBottom, ISCROLL_CHANGED,
&OBSBasicTransform::OnCropChanged);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
HookWidget(ui->cropToBounds, &QCheckBox::checkStateChanged,
&OBSBasicTransform::OnControlChanged);
#else
HookWidget(ui->cropToBounds, &QCheckBox::stateChanged,
&OBSBasicTransform::OnControlChanged);
#endif
ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
connect(ui->buttonBox->button(QDialogButtonBox::Reset),
&QPushButton::clicked, main,
&OBSBasic::on_actionResetTransform_triggered);
installEventFilter(CreateShortcutFilter());
OBSScene scene = obs_sceneitem_get_scene(item);
SetScene(scene);
SetItem(item);
std::string name = obs_source_get_name(obs_sceneitem_get_source(item));
setWindowTitle(QTStr("Basic.TransformWindow.Title").arg(name.c_str()));
OBSDataAutoRelease wrapper =
obs_scene_save_transform_states(main->GetCurrentScene(), false);
undo_data = std::string(obs_data_get_json(wrapper));
channelChangedSignal.Connect(obs_get_signal_handler(), "channel_change",
OBSChannelChanged, this);
}
OBSBasicTransform::~OBSBasicTransform()
{
OBSDataAutoRelease wrapper =
obs_scene_save_transform_states(main->GetCurrentScene(), false);
auto undo_redo = [](const std::string &data) {
OBSDataAutoRelease dat =
obs_data_create_from_json(data.c_str());
OBSSourceAutoRelease source = obs_get_source_by_uuid(
obs_data_get_string(dat, "scene_uuid"));
reinterpret_cast<OBSBasic *>(App()->GetMainWindow())
->SetCurrentScene(source.Get(), true);
obs_scene_load_transform_states(data.c_str());
};
std::string redo_data(obs_data_get_json(wrapper));
if (undo_data.compare(redo_data) != 0)
main->undo_s.add_action(
QTStr("Undo.Transform")
.arg(obs_source_get_name(obs_scene_get_source(
main->GetCurrentScene()))),
undo_redo, undo_redo, undo_data, redo_data);
}
void OBSBasicTransform::SetScene(OBSScene scene)
{
sigs.clear();
if (scene) {
OBSSource source = obs_scene_get_source(scene);
signal_handler_t *signal =
obs_source_get_signal_handler(source);
sigs.emplace_back(signal, "item_transform",
OBSSceneItemTransform, this);
sigs.emplace_back(signal, "item_remove", OBSSceneItemRemoved,
this);
sigs.emplace_back(signal, "item_select", OBSSceneItemSelect,
this);
sigs.emplace_back(signal, "item_deselect", OBSSceneItemDeselect,
this);
sigs.emplace_back(signal, "item_locked", OBSSceneItemLocked,
this);
}
}
void OBSBasicTransform::SetItem(OBSSceneItem newItem)
{
QMetaObject::invokeMethod(this, "SetItemQt",
Q_ARG(OBSSceneItem, OBSSceneItem(newItem)));
}
void OBSBasicTransform::SetEnabled(bool enable)
{
ui->container->setEnabled(enable);
ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(enable);
}
void OBSBasicTransform::SetItemQt(OBSSceneItem newItem)
{
item = newItem;
if (item)
RefreshControls();
bool enable = !!item && !obs_sceneitem_locked(item);
SetEnabled(enable);
}
void OBSBasicTransform::OBSChannelChanged(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
uint32_t channel = (uint32_t)calldata_int(data, "channel");
OBSSource source = (obs_source_t *)calldata_ptr(data, "source");
if (channel == 0) {
OBSScene scene = obs_scene_from_source(source);
window->SetScene(scene);
if (!scene)
window->SetItem(nullptr);
else
window->SetItem(FindASelectedItem(scene));
}
}
void OBSBasicTransform::OBSSceneItemTransform(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
OBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, "item");
if (item == window->item && !window->ignoreTransformSignal)
QMetaObject::invokeMethod(window, "RefreshControls");
}
void OBSBasicTransform::OBSSceneItemRemoved(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
obs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, "scene");
obs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, "item");
if (item == window->item)
window->SetItem(FindASelectedItem(scene));
}
void OBSBasicTransform::OBSSceneItemSelect(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
OBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, "item");
if (item != window->item)
window->SetItem(item);
}
void OBSBasicTransform::OBSSceneItemDeselect(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
obs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, "scene");
obs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, "item");
if (item == window->item) {
window->setWindowTitle(
QTStr("Basic.TransformWindow.NoSelectedSource"));
window->SetItem(FindASelectedItem(scene));
}
}
void OBSBasicTransform::OBSSceneItemLocked(void *param, calldata_t *data)
{
OBSBasicTransform *window =
reinterpret_cast<OBSBasicTransform *>(param);
bool locked = calldata_bool(data, "locked");
QMetaObject::invokeMethod(window, "SetEnabled", Q_ARG(bool, !locked));
}
static const uint32_t listToAlign[] = {OBS_ALIGN_TOP | OBS_ALIGN_LEFT,
OBS_ALIGN_TOP,
OBS_ALIGN_TOP | OBS_ALIGN_RIGHT,
OBS_ALIGN_LEFT,
OBS_ALIGN_CENTER,
OBS_ALIGN_RIGHT,
OBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT,
OBS_ALIGN_BOTTOM,
OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT};
static int AlignToList(uint32_t align)
{
int index = 0;
for (uint32_t curAlign : listToAlign) {
if (curAlign == align)
return index;
index++;
}
return 0;
}
void OBSBasicTransform::RefreshControls()
{
if (!item)
return;
obs_transform_info osi;
obs_sceneitem_crop crop;
obs_sceneitem_get_info2(item, &osi);
obs_sceneitem_get_crop(item, &crop);
obs_source_t *source = obs_sceneitem_get_source(item);
uint32_t source_cx = obs_source_get_width(source);
uint32_t source_cy = obs_source_get_height(source);
float width = float(source_cx);
float height = float(source_cy);
int alignIndex = AlignToList(osi.alignment);
int boundsAlignIndex = AlignToList(osi.bounds_alignment);
ignoreItemChange = true;
ui->positionX->setValue(osi.pos.x);
ui->positionY->setValue(osi.pos.y);
ui->rotation->setValue(osi.rot);
ui->sizeX->setValue(osi.scale.x * width);
ui->sizeY->setValue(osi.scale.y * height);
ui->align->setCurrentIndex(alignIndex);
bool valid_size = source_cx != 0 && source_cy != 0;
ui->sizeX->setEnabled(valid_size);
ui->sizeY->setEnabled(valid_size);
ui->boundsType->setCurrentIndex(int(osi.bounds_type));
ui->boundsAlign->setCurrentIndex(boundsAlignIndex);
ui->boundsWidth->setValue(osi.bounds.x);
ui->boundsHeight->setValue(osi.bounds.y);
ui->cropToBounds->setChecked(osi.crop_to_bounds);
ui->cropLeft->setValue(int(crop.left));
ui->cropRight->setValue(int(crop.right));
ui->cropTop->setValue(int(crop.top));
ui->cropBottom->setValue(int(crop.bottom));
ignoreItemChange = false;
std::string name = obs_source_get_name(source);
setWindowTitle(QTStr("Basic.TransformWindow.Title").arg(name.c_str()));
}
void OBSBasicTransform::OnBoundsType(int index)
{
if (index == -1)
return;
obs_bounds_type type = (obs_bounds_type)index;
bool enable = (type != OBS_BOUNDS_NONE);
ui->boundsAlign->setEnabled(enable);
ui->boundsWidth->setEnabled(enable);
ui->boundsHeight->setEnabled(enable);
ui->cropToBounds->setEnabled(enable);
if (!ignoreItemChange) {
obs_bounds_type lastType = obs_sceneitem_get_bounds_type(item);
if (lastType == OBS_BOUNDS_NONE) {
OBSSource source = obs_sceneitem_get_source(item);
int width = (int)obs_source_get_width(source);
int height = (int)obs_source_get_height(source);
ui->boundsWidth->setValue(width);
ui->boundsHeight->setValue(height);
}
}
OnControlChanged();
}
void OBSBasicTransform::OnControlChanged()
{
if (ignoreItemChange)
return;
obs_source_t *source = obs_sceneitem_get_source(item);
uint32_t source_cx = obs_source_get_width(source);
uint32_t source_cy = obs_source_get_height(source);
double width = double(source_cx);
double height = double(source_cy);
obs_transform_info oti;
obs_sceneitem_get_info2(item, &oti);
/* do not scale a source if it has 0 width/height */
if (source_cx != 0 && source_cy != 0) {
oti.scale.x = float(ui->sizeX->value() / width);
oti.scale.y = float(ui->sizeY->value() / height);
}
oti.pos.x = float(ui->positionX->value());
oti.pos.y = float(ui->positionY->value());
oti.rot = float(ui->rotation->value());
oti.alignment = listToAlign[ui->align->currentIndex()];
oti.bounds_type = (obs_bounds_type)ui->boundsType->currentIndex();
oti.bounds_alignment = listToAlign[ui->boundsAlign->currentIndex()];
oti.bounds.x = float(ui->boundsWidth->value());
oti.bounds.y = float(ui->boundsHeight->value());
oti.crop_to_bounds = ui->cropToBounds->isChecked();
ignoreTransformSignal = true;
obs_sceneitem_set_info2(item, &oti);
ignoreTransformSignal = false;
}
void OBSBasicTransform::OnCropChanged()
{
if (ignoreItemChange)
return;
obs_sceneitem_crop crop;
crop.left = uint32_t(ui->cropLeft->value());
crop.right = uint32_t(ui->cropRight->value());
crop.top = uint32_t(ui->cropTop->value());
crop.bottom = uint32_t(ui->cropBottom->value());
ignoreTransformSignal = true;
obs_sceneitem_set_crop(item, &crop);
ignoreTransformSignal = false;
}
template<typename T> static T GetOBSRef(QListWidgetItem *item)
{
return item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();
}
void OBSBasicTransform::OnSceneChanged(QListWidgetItem *current,
QListWidgetItem *)
{
if (!current)
return;
OBSScene scene = GetOBSRef<OBSScene>(current);
this->SetScene(scene);
}