1
0

OBSBasicTransform.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #include "OBSBasicTransform.hpp"
  2. #include <widgets/OBSBasic.hpp>
  3. #include "moc_OBSBasicTransform.cpp"
  4. static bool find_sel(obs_scene_t *, obs_sceneitem_t *item, void *param)
  5. {
  6. OBSSceneItem &dst = *reinterpret_cast<OBSSceneItem *>(param);
  7. if (obs_sceneitem_selected(item)) {
  8. dst = item;
  9. return false;
  10. }
  11. if (obs_sceneitem_is_group(item)) {
  12. obs_sceneitem_group_enum_items(item, find_sel, param);
  13. if (!!dst) {
  14. return false;
  15. }
  16. }
  17. return true;
  18. };
  19. static OBSSceneItem FindASelectedItem(obs_scene_t *scene)
  20. {
  21. OBSSceneItem item;
  22. obs_scene_enum_items(scene, find_sel, &item);
  23. return item;
  24. }
  25. #define COMBO_CHANGED &QComboBox::currentIndexChanged
  26. #define ISCROLL_CHANGED &QSpinBox::valueChanged
  27. #define DSCROLL_CHANGED &QDoubleSpinBox::valueChanged
  28. OBSBasicTransform::OBSBasicTransform(OBSSceneItem item, OBSBasic *parent)
  29. : QDialog(parent),
  30. ui(new Ui::OBSBasicTransform),
  31. main(parent)
  32. {
  33. setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
  34. ui->setupUi(this);
  35. HookWidget(ui->positionX, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  36. HookWidget(ui->positionY, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  37. HookWidget(ui->rotation, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  38. HookWidget(ui->sizeX, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  39. HookWidget(ui->sizeY, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  40. HookWidget(ui->align, COMBO_CHANGED, &OBSBasicTransform::OnControlChanged);
  41. HookWidget(ui->boundsType, COMBO_CHANGED, &OBSBasicTransform::OnBoundsType);
  42. HookWidget(ui->boundsAlign, COMBO_CHANGED, &OBSBasicTransform::OnControlChanged);
  43. HookWidget(ui->boundsWidth, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  44. HookWidget(ui->boundsHeight, DSCROLL_CHANGED, &OBSBasicTransform::OnControlChanged);
  45. HookWidget(ui->cropLeft, ISCROLL_CHANGED, &OBSBasicTransform::OnCropChanged);
  46. HookWidget(ui->cropRight, ISCROLL_CHANGED, &OBSBasicTransform::OnCropChanged);
  47. HookWidget(ui->cropTop, ISCROLL_CHANGED, &OBSBasicTransform::OnCropChanged);
  48. HookWidget(ui->cropBottom, ISCROLL_CHANGED, &OBSBasicTransform::OnCropChanged);
  49. #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
  50. HookWidget(ui->cropToBounds, &QCheckBox::checkStateChanged, &OBSBasicTransform::OnControlChanged);
  51. #else
  52. HookWidget(ui->cropToBounds, &QCheckBox::stateChanged, &OBSBasicTransform::OnControlChanged);
  53. #endif
  54. ui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);
  55. connect(ui->buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, main,
  56. &OBSBasic::on_actionResetTransform_triggered);
  57. installEventFilter(CreateShortcutFilter());
  58. OBSScene scene = obs_sceneitem_get_scene(item);
  59. SetScene(scene);
  60. SetItem(item);
  61. std::string name = obs_source_get_name(obs_sceneitem_get_source(item));
  62. setWindowTitle(QTStr("Basic.TransformWindow.Title").arg(name.c_str()));
  63. OBSDataAutoRelease wrapper = obs_scene_save_transform_states(main->GetCurrentScene(), false);
  64. undo_data = std::string(obs_data_get_json(wrapper));
  65. channelChangedSignal.Connect(obs_get_signal_handler(), "channel_change", OBSChannelChanged, this);
  66. }
  67. OBSBasicTransform::~OBSBasicTransform()
  68. {
  69. OBSDataAutoRelease wrapper = obs_scene_save_transform_states(main->GetCurrentScene(), false);
  70. auto undo_redo = [](const std::string &data) {
  71. OBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());
  72. OBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, "scene_uuid"));
  73. reinterpret_cast<OBSBasic *>(App()->GetMainWindow())->SetCurrentScene(source.Get(), true);
  74. obs_scene_load_transform_states(data.c_str());
  75. };
  76. std::string redo_data(obs_data_get_json(wrapper));
  77. if (undo_data.compare(redo_data) != 0)
  78. main->undo_s.add_action(
  79. QTStr("Undo.Transform").arg(obs_source_get_name(obs_scene_get_source(main->GetCurrentScene()))),
  80. undo_redo, undo_redo, undo_data, redo_data);
  81. }
  82. void OBSBasicTransform::SetScene(OBSScene scene)
  83. {
  84. sigs.clear();
  85. if (scene) {
  86. OBSSource source = obs_scene_get_source(scene);
  87. signal_handler_t *signal = obs_source_get_signal_handler(source);
  88. sigs.emplace_back(signal, "item_transform", OBSSceneItemTransform, this);
  89. sigs.emplace_back(signal, "item_remove", OBSSceneItemRemoved, this);
  90. sigs.emplace_back(signal, "item_select", OBSSceneItemSelect, this);
  91. sigs.emplace_back(signal, "item_deselect", OBSSceneItemDeselect, this);
  92. sigs.emplace_back(signal, "item_locked", OBSSceneItemLocked, this);
  93. }
  94. }
  95. void OBSBasicTransform::SetItem(OBSSceneItem newItem)
  96. {
  97. QMetaObject::invokeMethod(this, "SetItemQt", Q_ARG(OBSSceneItem, OBSSceneItem(newItem)));
  98. }
  99. void OBSBasicTransform::SetEnabled(bool enable)
  100. {
  101. ui->container->setEnabled(enable);
  102. ui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(enable);
  103. }
  104. void OBSBasicTransform::SetItemQt(OBSSceneItem newItem)
  105. {
  106. item = newItem;
  107. if (item)
  108. RefreshControls();
  109. bool enable = !!item && !obs_sceneitem_locked(item);
  110. SetEnabled(enable);
  111. }
  112. void OBSBasicTransform::OBSChannelChanged(void *param, calldata_t *data)
  113. {
  114. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  115. uint32_t channel = (uint32_t)calldata_int(data, "channel");
  116. OBSSource source = (obs_source_t *)calldata_ptr(data, "source");
  117. if (channel == 0) {
  118. OBSScene scene = obs_scene_from_source(source);
  119. window->SetScene(scene);
  120. if (!scene)
  121. window->SetItem(nullptr);
  122. else
  123. window->SetItem(FindASelectedItem(scene));
  124. }
  125. }
  126. void OBSBasicTransform::OBSSceneItemTransform(void *param, calldata_t *data)
  127. {
  128. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  129. OBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, "item");
  130. if (item == window->item && !window->ignoreTransformSignal)
  131. QMetaObject::invokeMethod(window, "RefreshControls");
  132. }
  133. void OBSBasicTransform::OBSSceneItemRemoved(void *param, calldata_t *data)
  134. {
  135. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  136. obs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, "scene");
  137. obs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, "item");
  138. if (item == window->item)
  139. window->SetItem(FindASelectedItem(scene));
  140. }
  141. void OBSBasicTransform::OBSSceneItemSelect(void *param, calldata_t *data)
  142. {
  143. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  144. OBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, "item");
  145. if (item != window->item)
  146. window->SetItem(item);
  147. }
  148. void OBSBasicTransform::OBSSceneItemDeselect(void *param, calldata_t *data)
  149. {
  150. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  151. obs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, "scene");
  152. obs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, "item");
  153. if (item == window->item) {
  154. window->setWindowTitle(QTStr("Basic.TransformWindow.NoSelectedSource"));
  155. window->SetItem(FindASelectedItem(scene));
  156. }
  157. }
  158. void OBSBasicTransform::OBSSceneItemLocked(void *param, calldata_t *data)
  159. {
  160. OBSBasicTransform *window = reinterpret_cast<OBSBasicTransform *>(param);
  161. bool locked = calldata_bool(data, "locked");
  162. QMetaObject::invokeMethod(window, "SetEnabled", Q_ARG(bool, !locked));
  163. }
  164. static const uint32_t listToAlign[] = {OBS_ALIGN_TOP | OBS_ALIGN_LEFT,
  165. OBS_ALIGN_TOP,
  166. OBS_ALIGN_TOP | OBS_ALIGN_RIGHT,
  167. OBS_ALIGN_LEFT,
  168. OBS_ALIGN_CENTER,
  169. OBS_ALIGN_RIGHT,
  170. OBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT,
  171. OBS_ALIGN_BOTTOM,
  172. OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT};
  173. static int AlignToList(uint32_t align)
  174. {
  175. int index = 0;
  176. for (uint32_t curAlign : listToAlign) {
  177. if (curAlign == align)
  178. return index;
  179. index++;
  180. }
  181. return 0;
  182. }
  183. void OBSBasicTransform::RefreshControls()
  184. {
  185. if (!item)
  186. return;
  187. obs_transform_info osi;
  188. obs_sceneitem_crop crop;
  189. obs_sceneitem_get_info2(item, &osi);
  190. obs_sceneitem_get_crop(item, &crop);
  191. obs_source_t *source = obs_sceneitem_get_source(item);
  192. uint32_t source_cx = obs_source_get_width(source);
  193. uint32_t source_cy = obs_source_get_height(source);
  194. float width = float(source_cx);
  195. float height = float(source_cy);
  196. int alignIndex = AlignToList(osi.alignment);
  197. int boundsAlignIndex = AlignToList(osi.bounds_alignment);
  198. ignoreItemChange = true;
  199. ui->positionX->setValue(osi.pos.x);
  200. ui->positionY->setValue(osi.pos.y);
  201. ui->rotation->setValue(osi.rot);
  202. ui->sizeX->setValue(osi.scale.x * width);
  203. ui->sizeY->setValue(osi.scale.y * height);
  204. ui->align->setCurrentIndex(alignIndex);
  205. bool valid_size = source_cx != 0 && source_cy != 0;
  206. ui->sizeX->setEnabled(valid_size);
  207. ui->sizeY->setEnabled(valid_size);
  208. ui->boundsType->setCurrentIndex(int(osi.bounds_type));
  209. ui->boundsAlign->setCurrentIndex(boundsAlignIndex);
  210. ui->boundsWidth->setValue(osi.bounds.x);
  211. ui->boundsHeight->setValue(osi.bounds.y);
  212. ui->cropToBounds->setChecked(osi.crop_to_bounds);
  213. ui->cropLeft->setValue(int(crop.left));
  214. ui->cropRight->setValue(int(crop.right));
  215. ui->cropTop->setValue(int(crop.top));
  216. ui->cropBottom->setValue(int(crop.bottom));
  217. ignoreItemChange = false;
  218. std::string name = obs_source_get_name(source);
  219. setWindowTitle(QTStr("Basic.TransformWindow.Title").arg(name.c_str()));
  220. }
  221. void OBSBasicTransform::OnBoundsType(int index)
  222. {
  223. if (index == -1)
  224. return;
  225. obs_bounds_type type = (obs_bounds_type)index;
  226. bool enable = (type != OBS_BOUNDS_NONE);
  227. ui->boundsAlign->setEnabled(enable);
  228. ui->boundsWidth->setEnabled(enable);
  229. ui->boundsHeight->setEnabled(enable);
  230. ui->cropToBounds->setEnabled(enable);
  231. if (!ignoreItemChange) {
  232. obs_bounds_type lastType = obs_sceneitem_get_bounds_type(item);
  233. if (lastType == OBS_BOUNDS_NONE) {
  234. OBSSource source = obs_sceneitem_get_source(item);
  235. int width = (int)obs_source_get_width(source);
  236. int height = (int)obs_source_get_height(source);
  237. ui->boundsWidth->setValue(width);
  238. ui->boundsHeight->setValue(height);
  239. }
  240. }
  241. OnControlChanged();
  242. }
  243. void OBSBasicTransform::OnControlChanged()
  244. {
  245. if (ignoreItemChange)
  246. return;
  247. obs_source_t *source = obs_sceneitem_get_source(item);
  248. uint32_t source_cx = obs_source_get_width(source);
  249. uint32_t source_cy = obs_source_get_height(source);
  250. double width = double(source_cx);
  251. double height = double(source_cy);
  252. obs_transform_info oti;
  253. obs_sceneitem_get_info2(item, &oti);
  254. /* do not scale a source if it has 0 width/height */
  255. if (source_cx != 0 && source_cy != 0) {
  256. oti.scale.x = float(ui->sizeX->value() / width);
  257. oti.scale.y = float(ui->sizeY->value() / height);
  258. }
  259. oti.pos.x = float(ui->positionX->value());
  260. oti.pos.y = float(ui->positionY->value());
  261. oti.rot = float(ui->rotation->value());
  262. oti.alignment = listToAlign[ui->align->currentIndex()];
  263. oti.bounds_type = (obs_bounds_type)ui->boundsType->currentIndex();
  264. oti.bounds_alignment = listToAlign[ui->boundsAlign->currentIndex()];
  265. oti.bounds.x = float(ui->boundsWidth->value());
  266. oti.bounds.y = float(ui->boundsHeight->value());
  267. oti.crop_to_bounds = ui->cropToBounds->isChecked();
  268. ignoreTransformSignal = true;
  269. obs_sceneitem_set_info2(item, &oti);
  270. ignoreTransformSignal = false;
  271. }
  272. void OBSBasicTransform::OnCropChanged()
  273. {
  274. if (ignoreItemChange)
  275. return;
  276. obs_sceneitem_crop crop;
  277. crop.left = uint32_t(ui->cropLeft->value());
  278. crop.right = uint32_t(ui->cropRight->value());
  279. crop.top = uint32_t(ui->cropTop->value());
  280. crop.bottom = uint32_t(ui->cropBottom->value());
  281. ignoreTransformSignal = true;
  282. obs_sceneitem_set_crop(item, &crop);
  283. ignoreTransformSignal = false;
  284. }
  285. void OBSBasicTransform::OnSceneChanged(QListWidgetItem *current, QListWidgetItem *)
  286. {
  287. if (!current)
  288. return;
  289. OBSScene scene = GetOBSRef<OBSScene>(current);
  290. this->SetScene(scene);
  291. }