window-basic-transform.cpp 11 KB

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