window-basic-preview.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943
  1. #include <QGuiApplication>
  2. #include <QMouseEvent>
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <string>
  6. #include <graphics/vec4.h>
  7. #include <graphics/matrix4.h>
  8. #include "window-basic-preview.hpp"
  9. #include "window-basic-main.hpp"
  10. #include "obs-app.hpp"
  11. #include "platform.hpp"
  12. #define HANDLE_RADIUS 4.0f
  13. #define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f)
  14. #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
  15. #define SUPPORTS_FRACTIONAL_SCALING
  16. #endif
  17. /* TODO: make C++ math classes and clean up code here later */
  18. OBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags)
  19. : OBSQTDisplay(parent, flags)
  20. {
  21. ResetScrollingOffset();
  22. setMouseTracking(true);
  23. }
  24. OBSBasicPreview::~OBSBasicPreview()
  25. {
  26. obs_enter_graphics();
  27. if (overflow)
  28. gs_texture_destroy(overflow);
  29. if (rectFill)
  30. gs_vertexbuffer_destroy(rectFill);
  31. obs_leave_graphics();
  32. }
  33. vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)
  34. {
  35. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  36. #ifdef SUPPORTS_FRACTIONAL_SCALING
  37. float pixelRatio = main->devicePixelRatioF();
  38. #else
  39. float pixelRatio = main->devicePixelRatio();
  40. #endif
  41. float scale = pixelRatio / main->previewScale;
  42. vec2 pos;
  43. vec2_set(&pos,
  44. (float(event->x()) - main->previewX / pixelRatio) * scale,
  45. (float(event->y()) - main->previewY / pixelRatio) * scale);
  46. return pos;
  47. }
  48. struct SceneFindData {
  49. const vec2 &pos;
  50. OBSSceneItem item;
  51. bool selectBelow;
  52. obs_sceneitem_t *group = nullptr;
  53. SceneFindData(const SceneFindData &) = delete;
  54. SceneFindData(SceneFindData &&) = delete;
  55. SceneFindData &operator=(const SceneFindData &) = delete;
  56. SceneFindData &operator=(SceneFindData &&) = delete;
  57. inline SceneFindData(const vec2 &pos_, bool selectBelow_)
  58. : pos(pos_), selectBelow(selectBelow_)
  59. {
  60. }
  61. };
  62. struct SceneFindBoxData {
  63. const vec2 &startPos;
  64. const vec2 &pos;
  65. std::vector<obs_sceneitem_t *> sceneItems;
  66. SceneFindBoxData(const SceneFindData &) = delete;
  67. SceneFindBoxData(SceneFindData &&) = delete;
  68. SceneFindBoxData &operator=(const SceneFindData &) = delete;
  69. SceneFindBoxData &operator=(SceneFindData &&) = delete;
  70. inline SceneFindBoxData(const vec2 &startPos_, const vec2 &pos_)
  71. : startPos(startPos_), pos(pos_)
  72. {
  73. }
  74. };
  75. static bool SceneItemHasVideo(obs_sceneitem_t *item)
  76. {
  77. obs_source_t *source = obs_sceneitem_get_source(item);
  78. uint32_t flags = obs_source_get_output_flags(source);
  79. return (flags & OBS_SOURCE_VIDEO) != 0;
  80. }
  81. static bool CloseFloat(float a, float b, float epsilon = 0.01)
  82. {
  83. using std::abs;
  84. return abs(a - b) <= epsilon;
  85. }
  86. static bool FindItemAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
  87. void *param)
  88. {
  89. SceneFindData *data = reinterpret_cast<SceneFindData *>(param);
  90. matrix4 transform;
  91. matrix4 invTransform;
  92. vec3 transformedPos;
  93. vec3 pos3;
  94. vec3 pos3_;
  95. if (!SceneItemHasVideo(item))
  96. return true;
  97. if (obs_sceneitem_locked(item))
  98. return true;
  99. vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
  100. obs_sceneitem_get_box_transform(item, &transform);
  101. matrix4_inv(&invTransform, &transform);
  102. vec3_transform(&transformedPos, &pos3, &invTransform);
  103. vec3_transform(&pos3_, &transformedPos, &transform);
  104. if (CloseFloat(pos3.x, pos3_.x) && CloseFloat(pos3.y, pos3_.y) &&
  105. transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
  106. transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
  107. if (data->selectBelow && obs_sceneitem_selected(item)) {
  108. if (data->item)
  109. return false;
  110. else
  111. data->selectBelow = false;
  112. }
  113. data->item = item;
  114. }
  115. UNUSED_PARAMETER(scene);
  116. return true;
  117. }
  118. static vec3 GetTransformedPos(float x, float y, const matrix4 &mat)
  119. {
  120. vec3 result;
  121. vec3_set(&result, x, y, 0.0f);
  122. vec3_transform(&result, &result, &mat);
  123. return result;
  124. }
  125. static inline vec2 GetOBSScreenSize()
  126. {
  127. obs_video_info ovi;
  128. vec2 size;
  129. vec2_zero(&size);
  130. if (obs_get_video_info(&ovi)) {
  131. size.x = float(ovi.base_width);
  132. size.y = float(ovi.base_height);
  133. }
  134. return size;
  135. }
  136. vec3 OBSBasicPreview::GetSnapOffset(const vec3 &tl, const vec3 &br)
  137. {
  138. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  139. vec2 screenSize = GetOBSScreenSize();
  140. vec3 clampOffset;
  141. vec3_zero(&clampOffset);
  142. const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow",
  143. "SnappingEnabled");
  144. if (snap == false)
  145. return clampOffset;
  146. const bool screenSnap = config_get_bool(
  147. GetGlobalConfig(), "BasicWindow", "ScreenSnapping");
  148. const bool centerSnap = config_get_bool(
  149. GetGlobalConfig(), "BasicWindow", "CenterSnapping");
  150. const float clampDist = config_get_double(GetGlobalConfig(),
  151. "BasicWindow",
  152. "SnapDistance") /
  153. main->previewScale;
  154. const float centerX = br.x - (br.x - tl.x) / 2.0f;
  155. const float centerY = br.y - (br.y - tl.y) / 2.0f;
  156. // Left screen edge.
  157. if (screenSnap && fabsf(tl.x) < clampDist)
  158. clampOffset.x = -tl.x;
  159. // Right screen edge.
  160. if (screenSnap && fabsf(clampOffset.x) < EPSILON &&
  161. fabsf(screenSize.x - br.x) < clampDist)
  162. clampOffset.x = screenSize.x - br.x;
  163. // Horizontal center.
  164. if (centerSnap && fabsf(screenSize.x - (br.x - tl.x)) > clampDist &&
  165. fabsf(screenSize.x / 2.0f - centerX) < clampDist)
  166. clampOffset.x = screenSize.x / 2.0f - centerX;
  167. // Top screen edge.
  168. if (screenSnap && fabsf(tl.y) < clampDist)
  169. clampOffset.y = -tl.y;
  170. // Bottom screen edge.
  171. if (screenSnap && fabsf(clampOffset.y) < EPSILON &&
  172. fabsf(screenSize.y - br.y) < clampDist)
  173. clampOffset.y = screenSize.y - br.y;
  174. // Vertical center.
  175. if (centerSnap && fabsf(screenSize.y - (br.y - tl.y)) > clampDist &&
  176. fabsf(screenSize.y / 2.0f - centerY) < clampDist)
  177. clampOffset.y = screenSize.y / 2.0f - centerY;
  178. return clampOffset;
  179. }
  180. OBSSceneItem OBSBasicPreview::GetItemAtPos(const vec2 &pos, bool selectBelow)
  181. {
  182. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  183. OBSScene scene = main->GetCurrentScene();
  184. if (!scene)
  185. return OBSSceneItem();
  186. SceneFindData data(pos, selectBelow);
  187. obs_scene_enum_items(scene, FindItemAtPos, &data);
  188. return data.item;
  189. }
  190. static bool CheckItemSelected(obs_scene_t *scene, obs_sceneitem_t *item,
  191. void *param)
  192. {
  193. SceneFindData *data = reinterpret_cast<SceneFindData *>(param);
  194. matrix4 transform;
  195. vec3 transformedPos;
  196. vec3 pos3;
  197. if (!SceneItemHasVideo(item))
  198. return true;
  199. if (obs_sceneitem_is_group(item)) {
  200. data->group = item;
  201. obs_sceneitem_group_enum_items(item, CheckItemSelected, param);
  202. data->group = nullptr;
  203. if (data->item) {
  204. return false;
  205. }
  206. }
  207. vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
  208. obs_sceneitem_get_box_transform(item, &transform);
  209. if (data->group) {
  210. matrix4 parent_transform;
  211. obs_sceneitem_get_draw_transform(data->group,
  212. &parent_transform);
  213. matrix4_mul(&transform, &transform, &parent_transform);
  214. }
  215. matrix4_inv(&transform, &transform);
  216. vec3_transform(&transformedPos, &pos3, &transform);
  217. if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
  218. transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
  219. if (obs_sceneitem_selected(item)) {
  220. data->item = item;
  221. return false;
  222. }
  223. }
  224. UNUSED_PARAMETER(scene);
  225. return true;
  226. }
  227. bool OBSBasicPreview::SelectedAtPos(const vec2 &pos)
  228. {
  229. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  230. OBSScene scene = main->GetCurrentScene();
  231. if (!scene)
  232. return false;
  233. SceneFindData data(pos, false);
  234. obs_scene_enum_items(scene, CheckItemSelected, &data);
  235. return !!data.item;
  236. }
  237. struct HandleFindData {
  238. const vec2 &pos;
  239. const float radius;
  240. matrix4 parent_xform;
  241. OBSSceneItem item;
  242. ItemHandle handle = ItemHandle::None;
  243. HandleFindData(const HandleFindData &) = delete;
  244. HandleFindData(HandleFindData &&) = delete;
  245. HandleFindData &operator=(const HandleFindData &) = delete;
  246. HandleFindData &operator=(HandleFindData &&) = delete;
  247. inline HandleFindData(const vec2 &pos_, float scale)
  248. : pos(pos_), radius(HANDLE_SEL_RADIUS / scale)
  249. {
  250. matrix4_identity(&parent_xform);
  251. }
  252. inline HandleFindData(const HandleFindData &hfd,
  253. obs_sceneitem_t *parent)
  254. : pos(hfd.pos),
  255. radius(hfd.radius),
  256. item(hfd.item),
  257. handle(hfd.handle)
  258. {
  259. obs_sceneitem_get_draw_transform(parent, &parent_xform);
  260. }
  261. };
  262. static bool FindHandleAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
  263. void *param)
  264. {
  265. HandleFindData &data = *reinterpret_cast<HandleFindData *>(param);
  266. if (!obs_sceneitem_selected(item)) {
  267. if (obs_sceneitem_is_group(item)) {
  268. HandleFindData newData(data, item);
  269. obs_sceneitem_group_enum_items(item, FindHandleAtPos,
  270. &newData);
  271. data.item = newData.item;
  272. data.handle = newData.handle;
  273. }
  274. return true;
  275. }
  276. matrix4 transform;
  277. vec3 pos3;
  278. float closestHandle = data.radius;
  279. vec3_set(&pos3, data.pos.x, data.pos.y, 0.0f);
  280. obs_sceneitem_get_box_transform(item, &transform);
  281. auto TestHandle = [&](float x, float y, ItemHandle handle) {
  282. vec3 handlePos = GetTransformedPos(x, y, transform);
  283. vec3_transform(&handlePos, &handlePos, &data.parent_xform);
  284. float dist = vec3_dist(&handlePos, &pos3);
  285. if (dist < data.radius) {
  286. if (dist < closestHandle) {
  287. closestHandle = dist;
  288. data.handle = handle;
  289. data.item = item;
  290. }
  291. }
  292. };
  293. TestHandle(0.0f, 0.0f, ItemHandle::TopLeft);
  294. TestHandle(0.5f, 0.0f, ItemHandle::TopCenter);
  295. TestHandle(1.0f, 0.0f, ItemHandle::TopRight);
  296. TestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);
  297. TestHandle(1.0f, 0.5f, ItemHandle::CenterRight);
  298. TestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);
  299. TestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);
  300. TestHandle(1.0f, 1.0f, ItemHandle::BottomRight);
  301. UNUSED_PARAMETER(scene);
  302. return true;
  303. }
  304. static vec2 GetItemSize(obs_sceneitem_t *item)
  305. {
  306. obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);
  307. vec2 size;
  308. if (boundsType != OBS_BOUNDS_NONE) {
  309. obs_sceneitem_get_bounds(item, &size);
  310. } else {
  311. obs_source_t *source = obs_sceneitem_get_source(item);
  312. obs_sceneitem_crop crop;
  313. vec2 scale;
  314. obs_sceneitem_get_scale(item, &scale);
  315. obs_sceneitem_get_crop(item, &crop);
  316. size.x = float(obs_source_get_width(source) - crop.left -
  317. crop.right) *
  318. scale.x;
  319. size.y = float(obs_source_get_height(source) - crop.top -
  320. crop.bottom) *
  321. scale.y;
  322. }
  323. return size;
  324. }
  325. void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
  326. {
  327. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  328. OBSScene scene = main->GetCurrentScene();
  329. if (!scene)
  330. return;
  331. #ifdef SUPPORTS_FRACTIONAL_SCALING
  332. float scale = main->previewScale / main->devicePixelRatioF();
  333. #else
  334. float scale = main->previewScale / main->devicePixelRatio();
  335. #endif
  336. vec2 scaled_pos = pos;
  337. vec2_divf(&scaled_pos, &scaled_pos, scale);
  338. HandleFindData data(scaled_pos, scale);
  339. obs_scene_enum_items(scene, FindHandleAtPos, &data);
  340. stretchItem = std::move(data.item);
  341. stretchHandle = data.handle;
  342. if (stretchHandle != ItemHandle::None) {
  343. matrix4 boxTransform;
  344. vec3 itemUL;
  345. float itemRot;
  346. stretchItemSize = GetItemSize(stretchItem);
  347. obs_sceneitem_get_box_transform(stretchItem, &boxTransform);
  348. itemRot = obs_sceneitem_get_rot(stretchItem);
  349. vec3_from_vec4(&itemUL, &boxTransform.t);
  350. /* build the item space conversion matrices */
  351. matrix4_identity(&itemToScreen);
  352. matrix4_rotate_aa4f(&itemToScreen, &itemToScreen, 0.0f, 0.0f,
  353. 1.0f, RAD(itemRot));
  354. matrix4_translate3f(&itemToScreen, &itemToScreen, itemUL.x,
  355. itemUL.y, 0.0f);
  356. matrix4_identity(&screenToItem);
  357. matrix4_translate3f(&screenToItem, &screenToItem, -itemUL.x,
  358. -itemUL.y, 0.0f);
  359. matrix4_rotate_aa4f(&screenToItem, &screenToItem, 0.0f, 0.0f,
  360. 1.0f, RAD(-itemRot));
  361. obs_sceneitem_get_crop(stretchItem, &startCrop);
  362. obs_sceneitem_get_pos(stretchItem, &startItemPos);
  363. obs_source_t *source = obs_sceneitem_get_source(stretchItem);
  364. cropSize.x = float(obs_source_get_width(source) -
  365. startCrop.left - startCrop.right);
  366. cropSize.y = float(obs_source_get_height(source) -
  367. startCrop.top - startCrop.bottom);
  368. stretchGroup = obs_sceneitem_get_group(scene, stretchItem);
  369. if (stretchGroup) {
  370. obs_sceneitem_get_draw_transform(stretchGroup,
  371. &invGroupTransform);
  372. matrix4_inv(&invGroupTransform, &invGroupTransform);
  373. obs_sceneitem_defer_group_resize_begin(stretchGroup);
  374. }
  375. }
  376. }
  377. void OBSBasicPreview::keyPressEvent(QKeyEvent *event)
  378. {
  379. if (!IsFixedScaling() || event->isAutoRepeat()) {
  380. OBSQTDisplay::keyPressEvent(event);
  381. return;
  382. }
  383. switch (event->key()) {
  384. case Qt::Key_Space:
  385. setCursor(Qt::OpenHandCursor);
  386. scrollMode = true;
  387. break;
  388. }
  389. OBSQTDisplay::keyPressEvent(event);
  390. }
  391. void OBSBasicPreview::keyReleaseEvent(QKeyEvent *event)
  392. {
  393. if (event->isAutoRepeat()) {
  394. OBSQTDisplay::keyReleaseEvent(event);
  395. return;
  396. }
  397. switch (event->key()) {
  398. case Qt::Key_Space:
  399. scrollMode = false;
  400. setCursor(Qt::ArrowCursor);
  401. break;
  402. }
  403. OBSQTDisplay::keyReleaseEvent(event);
  404. }
  405. void OBSBasicPreview::wheelEvent(QWheelEvent *event)
  406. {
  407. if (scrollMode && IsFixedScaling() &&
  408. event->orientation() == Qt::Vertical) {
  409. if (event->delta() > 0)
  410. SetScalingLevel(scalingLevel + 1);
  411. else if (event->delta() < 0)
  412. SetScalingLevel(scalingLevel - 1);
  413. emit DisplayResized();
  414. }
  415. OBSQTDisplay::wheelEvent(event);
  416. }
  417. void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
  418. {
  419. if (scrollMode && IsFixedScaling() &&
  420. event->button() == Qt::LeftButton) {
  421. setCursor(Qt::ClosedHandCursor);
  422. scrollingFrom.x = event->x();
  423. scrollingFrom.y = event->y();
  424. return;
  425. }
  426. if (event->button() == Qt::RightButton) {
  427. scrollMode = false;
  428. setCursor(Qt::ArrowCursor);
  429. }
  430. if (locked) {
  431. OBSQTDisplay::mousePressEvent(event);
  432. return;
  433. }
  434. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  435. #ifdef SUPPORTS_FRACTIONAL_SCALING
  436. float pixelRatio = main->devicePixelRatioF();
  437. #else
  438. float pixelRatio = main->devicePixelRatio();
  439. #endif
  440. float x = float(event->x()) - main->previewX / pixelRatio;
  441. float y = float(event->y()) - main->previewY / pixelRatio;
  442. Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
  443. bool altDown = (modifiers & Qt::AltModifier);
  444. bool shiftDown = (modifiers & Qt::ShiftModifier);
  445. bool ctrlDown = (modifiers & Qt::ControlModifier);
  446. OBSQTDisplay::mousePressEvent(event);
  447. if (event->button() != Qt::LeftButton &&
  448. event->button() != Qt::RightButton)
  449. return;
  450. if (event->button() == Qt::LeftButton)
  451. mouseDown = true;
  452. {
  453. std::lock_guard<std::mutex> lock(selectMutex);
  454. selectedItems.clear();
  455. }
  456. if (altDown)
  457. cropping = true;
  458. if (altDown || shiftDown || ctrlDown) {
  459. vec2 s;
  460. SceneFindBoxData data(s, s);
  461. obs_scene_enum_items(main->GetCurrentScene(), FindSelected,
  462. &data);
  463. std::lock_guard<std::mutex> lock(selectMutex);
  464. selectedItems = data.sceneItems;
  465. }
  466. vec2_set(&startPos, x, y);
  467. GetStretchHandleData(startPos);
  468. vec2_divf(&startPos, &startPos, main->previewScale / pixelRatio);
  469. startPos.x = std::round(startPos.x);
  470. startPos.y = std::round(startPos.y);
  471. mouseOverItems = SelectedAtPos(startPos);
  472. vec2_zero(&lastMoveOffset);
  473. mousePos = startPos;
  474. }
  475. static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
  476. {
  477. obs_sceneitem_t *selectedItem =
  478. reinterpret_cast<obs_sceneitem_t *>(param);
  479. if (obs_sceneitem_is_group(item))
  480. obs_sceneitem_group_enum_items(item, select_one, param);
  481. obs_sceneitem_select(item, (selectedItem == item));
  482. UNUSED_PARAMETER(scene);
  483. return true;
  484. }
  485. void OBSBasicPreview::DoSelect(const vec2 &pos)
  486. {
  487. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  488. OBSScene scene = main->GetCurrentScene();
  489. OBSSceneItem item = GetItemAtPos(pos, true);
  490. obs_scene_enum_items(scene, select_one, (obs_sceneitem_t *)item);
  491. }
  492. void OBSBasicPreview::DoCtrlSelect(const vec2 &pos)
  493. {
  494. OBSSceneItem item = GetItemAtPos(pos, false);
  495. if (!item)
  496. return;
  497. bool selected = obs_sceneitem_selected(item);
  498. obs_sceneitem_select(item, !selected);
  499. }
  500. void OBSBasicPreview::ProcessClick(const vec2 &pos)
  501. {
  502. Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
  503. if (modifiers & Qt::ControlModifier)
  504. DoCtrlSelect(pos);
  505. else
  506. DoSelect(pos);
  507. }
  508. void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
  509. {
  510. if (scrollMode)
  511. setCursor(Qt::OpenHandCursor);
  512. if (locked) {
  513. OBSQTDisplay::mouseReleaseEvent(event);
  514. return;
  515. }
  516. if (mouseDown) {
  517. vec2 pos = GetMouseEventPos(event);
  518. if (!mouseMoved)
  519. ProcessClick(pos);
  520. if (selectionBox) {
  521. Qt::KeyboardModifiers modifiers =
  522. QGuiApplication::keyboardModifiers();
  523. bool altDown = modifiers & Qt::AltModifier;
  524. bool shiftDown = modifiers & Qt::ShiftModifier;
  525. bool ctrlDown = modifiers & Qt::ControlModifier;
  526. std::lock_guard<std::mutex> lock(selectMutex);
  527. if (altDown || ctrlDown || shiftDown) {
  528. for (int i = 0; i < selectedItems.size(); i++) {
  529. obs_sceneitem_select(selectedItems[i],
  530. true);
  531. }
  532. }
  533. for (int i = 0; i < hoveredPreviewItems.size(); i++) {
  534. bool select = true;
  535. obs_sceneitem_t *item = hoveredPreviewItems[i];
  536. if (altDown) {
  537. select = false;
  538. } else if (ctrlDown) {
  539. select = !obs_sceneitem_selected(item);
  540. }
  541. obs_sceneitem_select(hoveredPreviewItems[i],
  542. select);
  543. }
  544. }
  545. if (stretchGroup) {
  546. obs_sceneitem_defer_group_resize_end(stretchGroup);
  547. }
  548. stretchItem = nullptr;
  549. stretchGroup = nullptr;
  550. mouseDown = false;
  551. mouseMoved = false;
  552. cropping = false;
  553. selectionBox = false;
  554. OBSSceneItem item = GetItemAtPos(pos, true);
  555. std::lock_guard<std::mutex> lock(selectMutex);
  556. hoveredPreviewItems.clear();
  557. hoveredPreviewItems.push_back(item);
  558. selectedItems.clear();
  559. }
  560. }
  561. struct SelectedItemBounds {
  562. bool first = true;
  563. vec3 tl, br;
  564. };
  565. static bool AddItemBounds(obs_scene_t *scene, obs_sceneitem_t *item,
  566. void *param)
  567. {
  568. SelectedItemBounds *data =
  569. reinterpret_cast<SelectedItemBounds *>(param);
  570. vec3 t[4];
  571. auto add_bounds = [data, &t]() {
  572. for (const vec3 &v : t) {
  573. if (data->first) {
  574. vec3_copy(&data->tl, &v);
  575. vec3_copy(&data->br, &v);
  576. data->first = false;
  577. } else {
  578. vec3_min(&data->tl, &data->tl, &v);
  579. vec3_max(&data->br, &data->br, &v);
  580. }
  581. }
  582. };
  583. if (obs_sceneitem_is_group(item)) {
  584. SelectedItemBounds sib;
  585. obs_sceneitem_group_enum_items(item, AddItemBounds, &sib);
  586. if (!sib.first) {
  587. matrix4 xform;
  588. obs_sceneitem_get_draw_transform(item, &xform);
  589. vec3_set(&t[0], sib.tl.x, sib.tl.y, 0.0f);
  590. vec3_set(&t[1], sib.tl.x, sib.br.y, 0.0f);
  591. vec3_set(&t[2], sib.br.x, sib.tl.y, 0.0f);
  592. vec3_set(&t[3], sib.br.x, sib.br.y, 0.0f);
  593. vec3_transform(&t[0], &t[0], &xform);
  594. vec3_transform(&t[1], &t[1], &xform);
  595. vec3_transform(&t[2], &t[2], &xform);
  596. vec3_transform(&t[3], &t[3], &xform);
  597. add_bounds();
  598. }
  599. }
  600. if (!obs_sceneitem_selected(item))
  601. return true;
  602. matrix4 boxTransform;
  603. obs_sceneitem_get_box_transform(item, &boxTransform);
  604. t[0] = GetTransformedPos(0.0f, 0.0f, boxTransform);
  605. t[1] = GetTransformedPos(1.0f, 0.0f, boxTransform);
  606. t[2] = GetTransformedPos(0.0f, 1.0f, boxTransform);
  607. t[3] = GetTransformedPos(1.0f, 1.0f, boxTransform);
  608. add_bounds();
  609. UNUSED_PARAMETER(scene);
  610. return true;
  611. }
  612. struct OffsetData {
  613. float clampDist;
  614. vec3 tl, br, offset;
  615. };
  616. static bool GetSourceSnapOffset(obs_scene_t *scene, obs_sceneitem_t *item,
  617. void *param)
  618. {
  619. OffsetData *data = reinterpret_cast<OffsetData *>(param);
  620. if (obs_sceneitem_selected(item))
  621. return true;
  622. matrix4 boxTransform;
  623. obs_sceneitem_get_box_transform(item, &boxTransform);
  624. vec3 t[4] = {GetTransformedPos(0.0f, 0.0f, boxTransform),
  625. GetTransformedPos(1.0f, 0.0f, boxTransform),
  626. GetTransformedPos(0.0f, 1.0f, boxTransform),
  627. GetTransformedPos(1.0f, 1.0f, boxTransform)};
  628. bool first = true;
  629. vec3 tl, br;
  630. vec3_zero(&tl);
  631. vec3_zero(&br);
  632. for (const vec3 &v : t) {
  633. if (first) {
  634. vec3_copy(&tl, &v);
  635. vec3_copy(&br, &v);
  636. first = false;
  637. } else {
  638. vec3_min(&tl, &tl, &v);
  639. vec3_max(&br, &br, &v);
  640. }
  641. }
  642. // Snap to other source edges
  643. #define EDGE_SNAP(l, r, x, y) \
  644. do { \
  645. double dist = fabsf(l.x - data->r.x); \
  646. if (dist < data->clampDist && \
  647. fabsf(data->offset.x) < EPSILON && data->tl.y < br.y && \
  648. data->br.y > tl.y && \
  649. (fabsf(data->offset.x) > dist || \
  650. data->offset.x < EPSILON)) \
  651. data->offset.x = l.x - data->r.x; \
  652. } while (false)
  653. EDGE_SNAP(tl, br, x, y);
  654. EDGE_SNAP(tl, br, y, x);
  655. EDGE_SNAP(br, tl, x, y);
  656. EDGE_SNAP(br, tl, y, x);
  657. #undef EDGE_SNAP
  658. UNUSED_PARAMETER(scene);
  659. return true;
  660. }
  661. void OBSBasicPreview::SnapItemMovement(vec2 &offset)
  662. {
  663. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  664. OBSScene scene = main->GetCurrentScene();
  665. SelectedItemBounds data;
  666. obs_scene_enum_items(scene, AddItemBounds, &data);
  667. data.tl.x += offset.x;
  668. data.tl.y += offset.y;
  669. data.br.x += offset.x;
  670. data.br.y += offset.y;
  671. vec3 snapOffset = GetSnapOffset(data.tl, data.br);
  672. const bool snap = config_get_bool(GetGlobalConfig(), "BasicWindow",
  673. "SnappingEnabled");
  674. const bool sourcesSnap = config_get_bool(
  675. GetGlobalConfig(), "BasicWindow", "SourceSnapping");
  676. if (snap == false)
  677. return;
  678. if (sourcesSnap == false) {
  679. offset.x += snapOffset.x;
  680. offset.y += snapOffset.y;
  681. return;
  682. }
  683. const float clampDist = config_get_double(GetGlobalConfig(),
  684. "BasicWindow",
  685. "SnapDistance") /
  686. main->previewScale;
  687. OffsetData offsetData;
  688. offsetData.clampDist = clampDist;
  689. offsetData.tl = data.tl;
  690. offsetData.br = data.br;
  691. vec3_copy(&offsetData.offset, &snapOffset);
  692. obs_scene_enum_items(scene, GetSourceSnapOffset, &offsetData);
  693. if (fabsf(offsetData.offset.x) > EPSILON ||
  694. fabsf(offsetData.offset.y) > EPSILON) {
  695. offset.x += offsetData.offset.x;
  696. offset.y += offsetData.offset.y;
  697. } else {
  698. offset.x += snapOffset.x;
  699. offset.y += snapOffset.y;
  700. }
  701. }
  702. static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
  703. {
  704. if (obs_sceneitem_locked(item))
  705. return true;
  706. bool selected = obs_sceneitem_selected(item);
  707. vec2 *offset = reinterpret_cast<vec2 *>(param);
  708. if (obs_sceneitem_is_group(item) && !selected) {
  709. matrix4 transform;
  710. vec3 new_offset;
  711. vec3_set(&new_offset, offset->x, offset->y, 0.0f);
  712. obs_sceneitem_get_draw_transform(item, &transform);
  713. vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
  714. matrix4_inv(&transform, &transform);
  715. vec3_transform(&new_offset, &new_offset, &transform);
  716. obs_sceneitem_group_enum_items(item, move_items, &new_offset);
  717. }
  718. if (selected) {
  719. vec2 pos;
  720. obs_sceneitem_get_pos(item, &pos);
  721. vec2_add(&pos, &pos, offset);
  722. obs_sceneitem_set_pos(item, &pos);
  723. }
  724. UNUSED_PARAMETER(scene);
  725. return true;
  726. }
  727. void OBSBasicPreview::MoveItems(const vec2 &pos)
  728. {
  729. Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
  730. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  731. OBSScene scene = main->GetCurrentScene();
  732. vec2 offset, moveOffset;
  733. vec2_sub(&offset, &pos, &startPos);
  734. vec2_sub(&moveOffset, &offset, &lastMoveOffset);
  735. if (!(modifiers & Qt::ControlModifier))
  736. SnapItemMovement(moveOffset);
  737. vec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset);
  738. obs_scene_enum_items(scene, move_items, &moveOffset);
  739. }
  740. static bool CounterClockwise(float x1, float x2, float x3, float y1, float y2,
  741. float y3)
  742. {
  743. return (y3 - y1) * (x2 - x1) > (y2 - y1) * (x3 - x1);
  744. }
  745. static bool IntersectLine(float x1, float x2, float x3, float x4, float y1,
  746. float y2, float y3, float y4)
  747. {
  748. bool a = CounterClockwise(x1, x2, x3, y1, y2, y3);
  749. bool b = CounterClockwise(x1, x2, x4, y1, y2, y4);
  750. bool c = CounterClockwise(x3, x4, x1, y3, y4, y1);
  751. bool d = CounterClockwise(x3, x4, x2, y3, y4, y2);
  752. return (a != b) && (c != d);
  753. }
  754. static bool IntersectBox(matrix4 transform, float x1, float x2, float y1,
  755. float y2)
  756. {
  757. float x3, x4, y3, y4;
  758. x3 = transform.t.x;
  759. y3 = transform.t.y;
  760. x4 = x3 + transform.x.x;
  761. y4 = y3 + transform.x.y;
  762. if (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) ||
  763. IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||
  764. IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) ||
  765. IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))
  766. return true;
  767. x4 = x3 + transform.y.x;
  768. y4 = y3 + transform.y.y;
  769. if (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) ||
  770. IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||
  771. IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) ||
  772. IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))
  773. return true;
  774. x3 = transform.t.x + transform.x.x;
  775. y3 = transform.t.y + transform.x.y;
  776. x4 = x3 + transform.y.x;
  777. y4 = y3 + transform.y.y;
  778. if (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) ||
  779. IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||
  780. IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) ||
  781. IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))
  782. return true;
  783. x3 = transform.t.x + transform.y.x;
  784. y3 = transform.t.y + transform.y.y;
  785. x4 = x3 + transform.x.x;
  786. y4 = y3 + transform.x.y;
  787. if (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) ||
  788. IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||
  789. IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) ||
  790. IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))
  791. return true;
  792. return false;
  793. }
  794. #undef PI
  795. bool OBSBasicPreview::FindSelected(obs_scene_t *scene, obs_sceneitem_t *item,
  796. void *param)
  797. {
  798. SceneFindBoxData *data = reinterpret_cast<SceneFindBoxData *>(param);
  799. if (obs_sceneitem_selected(item))
  800. data->sceneItems.push_back(item);
  801. UNUSED_PARAMETER(scene);
  802. return true;
  803. }
  804. static bool FindItemsInBox(obs_scene_t *scene, obs_sceneitem_t *item,
  805. void *param)
  806. {
  807. SceneFindBoxData *data = reinterpret_cast<SceneFindBoxData *>(param);
  808. matrix4 transform;
  809. matrix4 invTransform;
  810. vec3 transformedPos;
  811. vec3 pos3;
  812. vec3 pos3_;
  813. float x1 = std::min(data->startPos.x, data->pos.x);
  814. float x2 = std::max(data->startPos.x, data->pos.x);
  815. float y1 = std::min(data->startPos.y, data->pos.y);
  816. float y2 = std::max(data->startPos.y, data->pos.y);
  817. if (!SceneItemHasVideo(item))
  818. return true;
  819. if (obs_sceneitem_locked(item))
  820. return true;
  821. if (!obs_sceneitem_visible(item))
  822. return true;
  823. vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
  824. obs_sceneitem_get_box_transform(item, &transform);
  825. matrix4_inv(&invTransform, &transform);
  826. vec3_transform(&transformedPos, &pos3, &invTransform);
  827. vec3_transform(&pos3_, &transformedPos, &transform);
  828. if (CloseFloat(pos3.x, pos3_.x) && CloseFloat(pos3.y, pos3_.y) &&
  829. transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
  830. transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
  831. data->sceneItems.push_back(item);
  832. return true;
  833. }
  834. if (transform.t.x > x1 && transform.t.x < x2 && transform.t.y > y1 &&
  835. transform.t.y < y2) {
  836. data->sceneItems.push_back(item);
  837. return true;
  838. }
  839. if (transform.t.x + transform.x.x > x1 &&
  840. transform.t.x + transform.x.x < x2 &&
  841. transform.t.y + transform.x.y > y1 &&
  842. transform.t.y + transform.x.y < y2) {
  843. data->sceneItems.push_back(item);
  844. return true;
  845. }
  846. if (transform.t.x + transform.y.x > x1 &&
  847. transform.t.x + transform.y.x < x2 &&
  848. transform.t.y + transform.y.y > y1 &&
  849. transform.t.y + transform.y.y < y2) {
  850. data->sceneItems.push_back(item);
  851. return true;
  852. }
  853. if (transform.t.x + transform.x.x + transform.y.x > x1 &&
  854. transform.t.x + transform.x.x + transform.y.x < x2 &&
  855. transform.t.y + transform.x.y + transform.y.y > y1 &&
  856. transform.t.y + transform.x.y + transform.y.y < y2) {
  857. data->sceneItems.push_back(item);
  858. return true;
  859. }
  860. if (transform.t.x + 0.5 * (transform.x.x + transform.y.x) > x1 &&
  861. transform.t.x + 0.5 * (transform.x.x + transform.y.x) < x2 &&
  862. transform.t.y + 0.5 * (transform.x.y + transform.y.y) > y1 &&
  863. transform.t.y + 0.5 * (transform.x.y + transform.y.y) < y2) {
  864. data->sceneItems.push_back(item);
  865. return true;
  866. }
  867. if (IntersectBox(transform, x1, x2, y1, y2)) {
  868. data->sceneItems.push_back(item);
  869. return true;
  870. }
  871. UNUSED_PARAMETER(scene);
  872. return true;
  873. }
  874. void OBSBasicPreview::BoxItems(const vec2 &startPos, const vec2 &pos)
  875. {
  876. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  877. OBSScene scene = main->GetCurrentScene();
  878. if (!scene)
  879. return;
  880. SceneFindBoxData data(startPos, pos);
  881. obs_scene_enum_items(scene, FindItemsInBox, &data);
  882. std::lock_guard<std::mutex> lock(selectMutex);
  883. hoveredPreviewItems = data.sceneItems;
  884. }
  885. vec3 OBSBasicPreview::CalculateStretchPos(const vec3 &tl, const vec3 &br)
  886. {
  887. uint32_t alignment = obs_sceneitem_get_alignment(stretchItem);
  888. vec3 pos;
  889. vec3_zero(&pos);
  890. if (alignment & OBS_ALIGN_LEFT)
  891. pos.x = tl.x;
  892. else if (alignment & OBS_ALIGN_RIGHT)
  893. pos.x = br.x;
  894. else
  895. pos.x = (br.x - tl.x) * 0.5f + tl.x;
  896. if (alignment & OBS_ALIGN_TOP)
  897. pos.y = tl.y;
  898. else if (alignment & OBS_ALIGN_BOTTOM)
  899. pos.y = br.y;
  900. else
  901. pos.y = (br.y - tl.y) * 0.5f + tl.y;
  902. return pos;
  903. }
  904. void OBSBasicPreview::ClampAspect(vec3 &tl, vec3 &br, vec2 &size,
  905. const vec2 &baseSize)
  906. {
  907. float baseAspect = baseSize.x / baseSize.y;
  908. float aspect = size.x / size.y;
  909. uint32_t stretchFlags = (uint32_t)stretchHandle;
  910. if (stretchHandle == ItemHandle::TopLeft ||
  911. stretchHandle == ItemHandle::TopRight ||
  912. stretchHandle == ItemHandle::BottomLeft ||
  913. stretchHandle == ItemHandle::BottomRight) {
  914. if (aspect < baseAspect) {
  915. if ((size.y >= 0.0f && size.x >= 0.0f) ||
  916. (size.y <= 0.0f && size.x <= 0.0f))
  917. size.x = size.y * baseAspect;
  918. else
  919. size.x = size.y * baseAspect * -1.0f;
  920. } else {
  921. if ((size.y >= 0.0f && size.x >= 0.0f) ||
  922. (size.y <= 0.0f && size.x <= 0.0f))
  923. size.y = size.x / baseAspect;
  924. else
  925. size.y = size.x / baseAspect * -1.0f;
  926. }
  927. } else if (stretchHandle == ItemHandle::TopCenter ||
  928. stretchHandle == ItemHandle::BottomCenter) {
  929. if ((size.y >= 0.0f && size.x >= 0.0f) ||
  930. (size.y <= 0.0f && size.x <= 0.0f))
  931. size.x = size.y * baseAspect;
  932. else
  933. size.x = size.y * baseAspect * -1.0f;
  934. } else if (stretchHandle == ItemHandle::CenterLeft ||
  935. stretchHandle == ItemHandle::CenterRight) {
  936. if ((size.y >= 0.0f && size.x >= 0.0f) ||
  937. (size.y <= 0.0f && size.x <= 0.0f))
  938. size.y = size.x / baseAspect;
  939. else
  940. size.y = size.x / baseAspect * -1.0f;
  941. }
  942. size.x = std::round(size.x);
  943. size.y = std::round(size.y);
  944. if (stretchFlags & ITEM_LEFT)
  945. tl.x = br.x - size.x;
  946. else if (stretchFlags & ITEM_RIGHT)
  947. br.x = tl.x + size.x;
  948. if (stretchFlags & ITEM_TOP)
  949. tl.y = br.y - size.y;
  950. else if (stretchFlags & ITEM_BOTTOM)
  951. br.y = tl.y + size.y;
  952. }
  953. void OBSBasicPreview::SnapStretchingToScreen(vec3 &tl, vec3 &br)
  954. {
  955. uint32_t stretchFlags = (uint32_t)stretchHandle;
  956. vec3 newTL = GetTransformedPos(tl.x, tl.y, itemToScreen);
  957. vec3 newTR = GetTransformedPos(br.x, tl.y, itemToScreen);
  958. vec3 newBL = GetTransformedPos(tl.x, br.y, itemToScreen);
  959. vec3 newBR = GetTransformedPos(br.x, br.y, itemToScreen);
  960. vec3 boundingTL;
  961. vec3 boundingBR;
  962. vec3_copy(&boundingTL, &newTL);
  963. vec3_min(&boundingTL, &boundingTL, &newTR);
  964. vec3_min(&boundingTL, &boundingTL, &newBL);
  965. vec3_min(&boundingTL, &boundingTL, &newBR);
  966. vec3_copy(&boundingBR, &newTL);
  967. vec3_max(&boundingBR, &boundingBR, &newTR);
  968. vec3_max(&boundingBR, &boundingBR, &newBL);
  969. vec3_max(&boundingBR, &boundingBR, &newBR);
  970. vec3 offset = GetSnapOffset(boundingTL, boundingBR);
  971. vec3_add(&offset, &offset, &newTL);
  972. vec3_transform(&offset, &offset, &screenToItem);
  973. vec3_sub(&offset, &offset, &tl);
  974. if (stretchFlags & ITEM_LEFT)
  975. tl.x += offset.x;
  976. else if (stretchFlags & ITEM_RIGHT)
  977. br.x += offset.x;
  978. if (stretchFlags & ITEM_TOP)
  979. tl.y += offset.y;
  980. else if (stretchFlags & ITEM_BOTTOM)
  981. br.y += offset.y;
  982. }
  983. static float maxfunc(float x, float y)
  984. {
  985. return x > y ? x : y;
  986. }
  987. static float minfunc(float x, float y)
  988. {
  989. return x < y ? x : y;
  990. }
  991. void OBSBasicPreview::CropItem(const vec2 &pos)
  992. {
  993. obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
  994. uint32_t stretchFlags = (uint32_t)stretchHandle;
  995. uint32_t align = obs_sceneitem_get_alignment(stretchItem);
  996. vec3 tl, br, pos3;
  997. vec3_zero(&tl);
  998. vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
  999. vec3_set(&pos3, pos.x, pos.y, 0.0f);
  1000. vec3_transform(&pos3, &pos3, &screenToItem);
  1001. obs_sceneitem_crop crop = startCrop;
  1002. vec2 scale;
  1003. obs_sceneitem_get_scale(stretchItem, &scale);
  1004. vec2 max_tl;
  1005. vec2 max_br;
  1006. vec2_set(&max_tl, float(-crop.left) * scale.x,
  1007. float(-crop.top) * scale.y);
  1008. vec2_set(&max_br, stretchItemSize.x + crop.right * scale.x,
  1009. stretchItemSize.y + crop.bottom * scale.y);
  1010. typedef std::function<float(float, float)> minmax_func_t;
  1011. minmax_func_t min_x = scale.x < 0.0f ? maxfunc : minfunc;
  1012. minmax_func_t min_y = scale.y < 0.0f ? maxfunc : minfunc;
  1013. minmax_func_t max_x = scale.x < 0.0f ? minfunc : maxfunc;
  1014. minmax_func_t max_y = scale.y < 0.0f ? minfunc : maxfunc;
  1015. pos3.x = min_x(pos3.x, max_br.x);
  1016. pos3.x = max_x(pos3.x, max_tl.x);
  1017. pos3.y = min_y(pos3.y, max_br.y);
  1018. pos3.y = max_y(pos3.y, max_tl.y);
  1019. if (stretchFlags & ITEM_LEFT) {
  1020. float maxX = stretchItemSize.x - (2.0 * scale.x);
  1021. pos3.x = tl.x = min_x(pos3.x, maxX);
  1022. } else if (stretchFlags & ITEM_RIGHT) {
  1023. float minX = (2.0 * scale.x);
  1024. pos3.x = br.x = max_x(pos3.x, minX);
  1025. }
  1026. if (stretchFlags & ITEM_TOP) {
  1027. float maxY = stretchItemSize.y - (2.0 * scale.y);
  1028. pos3.y = tl.y = min_y(pos3.y, maxY);
  1029. } else if (stretchFlags & ITEM_BOTTOM) {
  1030. float minY = (2.0 * scale.y);
  1031. pos3.y = br.y = max_y(pos3.y, minY);
  1032. }
  1033. #define ALIGN_X (ITEM_LEFT | ITEM_RIGHT)
  1034. #define ALIGN_Y (ITEM_TOP | ITEM_BOTTOM)
  1035. vec3 newPos;
  1036. vec3_zero(&newPos);
  1037. uint32_t align_x = (align & ALIGN_X);
  1038. uint32_t align_y = (align & ALIGN_Y);
  1039. if (align_x == (stretchFlags & ALIGN_X) && align_x != 0)
  1040. newPos.x = pos3.x;
  1041. else if (align & ITEM_RIGHT)
  1042. newPos.x = stretchItemSize.x;
  1043. else if (!(align & ITEM_LEFT))
  1044. newPos.x = stretchItemSize.x * 0.5f;
  1045. if (align_y == (stretchFlags & ALIGN_Y) && align_y != 0)
  1046. newPos.y = pos3.y;
  1047. else if (align & ITEM_BOTTOM)
  1048. newPos.y = stretchItemSize.y;
  1049. else if (!(align & ITEM_TOP))
  1050. newPos.y = stretchItemSize.y * 0.5f;
  1051. #undef ALIGN_X
  1052. #undef ALIGN_Y
  1053. crop = startCrop;
  1054. if (stretchFlags & ITEM_LEFT)
  1055. crop.left += int(std::round(tl.x / scale.x));
  1056. else if (stretchFlags & ITEM_RIGHT)
  1057. crop.right +=
  1058. int(std::round((stretchItemSize.x - br.x) / scale.x));
  1059. if (stretchFlags & ITEM_TOP)
  1060. crop.top += int(std::round(tl.y / scale.y));
  1061. else if (stretchFlags & ITEM_BOTTOM)
  1062. crop.bottom +=
  1063. int(std::round((stretchItemSize.y - br.y) / scale.y));
  1064. vec3_transform(&newPos, &newPos, &itemToScreen);
  1065. newPos.x = std::round(newPos.x);
  1066. newPos.y = std::round(newPos.y);
  1067. #if 0
  1068. vec3 curPos;
  1069. vec3_zero(&curPos);
  1070. obs_sceneitem_get_pos(stretchItem, (vec2*)&curPos);
  1071. blog(LOG_DEBUG, "curPos {%d, %d} - newPos {%d, %d}",
  1072. int(curPos.x), int(curPos.y),
  1073. int(newPos.x), int(newPos.y));
  1074. blog(LOG_DEBUG, "crop {%d, %d, %d, %d}",
  1075. crop.left, crop.top,
  1076. crop.right, crop.bottom);
  1077. #endif
  1078. obs_sceneitem_defer_update_begin(stretchItem);
  1079. obs_sceneitem_set_crop(stretchItem, &crop);
  1080. if (boundsType == OBS_BOUNDS_NONE)
  1081. obs_sceneitem_set_pos(stretchItem, (vec2 *)&newPos);
  1082. obs_sceneitem_defer_update_end(stretchItem);
  1083. }
  1084. void OBSBasicPreview::StretchItem(const vec2 &pos)
  1085. {
  1086. Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
  1087. obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
  1088. uint32_t stretchFlags = (uint32_t)stretchHandle;
  1089. bool shiftDown = (modifiers & Qt::ShiftModifier);
  1090. vec3 tl, br, pos3;
  1091. vec3_zero(&tl);
  1092. vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
  1093. vec3_set(&pos3, pos.x, pos.y, 0.0f);
  1094. vec3_transform(&pos3, &pos3, &screenToItem);
  1095. if (stretchFlags & ITEM_LEFT)
  1096. tl.x = pos3.x;
  1097. else if (stretchFlags & ITEM_RIGHT)
  1098. br.x = pos3.x;
  1099. if (stretchFlags & ITEM_TOP)
  1100. tl.y = pos3.y;
  1101. else if (stretchFlags & ITEM_BOTTOM)
  1102. br.y = pos3.y;
  1103. if (!(modifiers & Qt::ControlModifier))
  1104. SnapStretchingToScreen(tl, br);
  1105. obs_source_t *source = obs_sceneitem_get_source(stretchItem);
  1106. vec2 baseSize;
  1107. vec2_set(&baseSize, float(obs_source_get_width(source)),
  1108. float(obs_source_get_height(source)));
  1109. vec2 size;
  1110. vec2_set(&size, br.x - tl.x, br.y - tl.y);
  1111. if (boundsType != OBS_BOUNDS_NONE) {
  1112. if (shiftDown)
  1113. ClampAspect(tl, br, size, baseSize);
  1114. if (tl.x > br.x)
  1115. std::swap(tl.x, br.x);
  1116. if (tl.y > br.y)
  1117. std::swap(tl.y, br.y);
  1118. vec2_abs(&size, &size);
  1119. obs_sceneitem_set_bounds(stretchItem, &size);
  1120. } else {
  1121. obs_sceneitem_crop crop;
  1122. obs_sceneitem_get_crop(stretchItem, &crop);
  1123. baseSize.x -= float(crop.left + crop.right);
  1124. baseSize.y -= float(crop.top + crop.bottom);
  1125. if (!shiftDown)
  1126. ClampAspect(tl, br, size, baseSize);
  1127. vec2_div(&size, &size, &baseSize);
  1128. obs_sceneitem_set_scale(stretchItem, &size);
  1129. }
  1130. pos3 = CalculateStretchPos(tl, br);
  1131. vec3_transform(&pos3, &pos3, &itemToScreen);
  1132. vec2 newPos;
  1133. vec2_set(&newPos, std::round(pos3.x), std::round(pos3.y));
  1134. obs_sceneitem_set_pos(stretchItem, &newPos);
  1135. }
  1136. void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
  1137. {
  1138. if (scrollMode && event->buttons() == Qt::LeftButton) {
  1139. scrollingOffset.x += event->x() - scrollingFrom.x;
  1140. scrollingOffset.y += event->y() - scrollingFrom.y;
  1141. scrollingFrom.x = event->x();
  1142. scrollingFrom.y = event->y();
  1143. emit DisplayResized();
  1144. return;
  1145. }
  1146. if (locked)
  1147. return;
  1148. if (mouseDown) {
  1149. vec2 pos = GetMouseEventPos(event);
  1150. if (!mouseMoved && !mouseOverItems &&
  1151. stretchHandle == ItemHandle::None) {
  1152. ProcessClick(startPos);
  1153. mouseOverItems = SelectedAtPos(startPos);
  1154. }
  1155. pos.x = std::round(pos.x);
  1156. pos.y = std::round(pos.y);
  1157. if (stretchHandle != ItemHandle::None) {
  1158. selectionBox = false;
  1159. OBSBasic *main = reinterpret_cast<OBSBasic *>(
  1160. App()->GetMainWindow());
  1161. OBSScene scene = main->GetCurrentScene();
  1162. obs_sceneitem_t *group =
  1163. obs_sceneitem_get_group(scene, stretchItem);
  1164. if (group) {
  1165. vec3 group_pos;
  1166. vec3_set(&group_pos, pos.x, pos.y, 0.0f);
  1167. vec3_transform(&group_pos, &group_pos,
  1168. &invGroupTransform);
  1169. pos.x = group_pos.x;
  1170. pos.y = group_pos.y;
  1171. }
  1172. if (cropping)
  1173. CropItem(pos);
  1174. else
  1175. StretchItem(pos);
  1176. } else if (mouseOverItems) {
  1177. selectionBox = false;
  1178. MoveItems(pos);
  1179. } else {
  1180. selectionBox = true;
  1181. if (!mouseMoved)
  1182. DoSelect(startPos);
  1183. BoxItems(startPos, pos);
  1184. }
  1185. mouseMoved = true;
  1186. mousePos = pos;
  1187. } else {
  1188. vec2 pos = GetMouseEventPos(event);
  1189. OBSSceneItem item = GetItemAtPos(pos, true);
  1190. std::lock_guard<std::mutex> lock(selectMutex);
  1191. hoveredPreviewItems.clear();
  1192. hoveredPreviewItems.push_back(item);
  1193. }
  1194. }
  1195. void OBSBasicPreview::leaveEvent(QEvent *)
  1196. {
  1197. std::lock_guard<std::mutex> lock(selectMutex);
  1198. if (!selectionBox)
  1199. hoveredPreviewItems.clear();
  1200. }
  1201. static void DrawSquareAtPos(float x, float y)
  1202. {
  1203. struct vec3 pos;
  1204. vec3_set(&pos, x, y, 0.0f);
  1205. struct matrix4 matrix;
  1206. gs_matrix_get(&matrix);
  1207. vec3_transform(&pos, &pos, &matrix);
  1208. gs_matrix_push();
  1209. gs_matrix_identity();
  1210. gs_matrix_translate(&pos);
  1211. gs_matrix_translate3f(-HANDLE_RADIUS, -HANDLE_RADIUS, 0.0f);
  1212. gs_matrix_scale3f(HANDLE_RADIUS * 2, HANDLE_RADIUS * 2, 1.0f);
  1213. gs_draw(GS_TRISTRIP, 0, 0);
  1214. gs_matrix_pop();
  1215. }
  1216. static void DrawLine(float x1, float y1, float x2, float y2, float thickness,
  1217. vec2 scale)
  1218. {
  1219. float ySide = (y1 == y2) ? (y1 < 0.5f ? 1.0f : -1.0f) : 0.0f;
  1220. float xSide = (x1 == x2) ? (x1 < 0.5f ? 1.0f : -1.0f) : 0.0f;
  1221. gs_render_start(true);
  1222. gs_vertex2f(x1, y1);
  1223. gs_vertex2f(x1 + (xSide * (thickness / scale.x)),
  1224. y1 + (ySide * (thickness / scale.y)));
  1225. gs_vertex2f(x2 + (xSide * (thickness / scale.x)),
  1226. y2 + (ySide * (thickness / scale.y)));
  1227. gs_vertex2f(x2, y2);
  1228. gs_vertex2f(x1, y1);
  1229. gs_vertbuffer_t *line = gs_render_save();
  1230. gs_load_vertexbuffer(line);
  1231. gs_draw(GS_TRISTRIP, 0, 0);
  1232. gs_vertexbuffer_destroy(line);
  1233. }
  1234. static void DrawRect(float thickness, vec2 scale)
  1235. {
  1236. gs_render_start(true);
  1237. gs_vertex2f(0.0f, 0.0f);
  1238. gs_vertex2f(0.0f + (thickness / scale.x), 0.0f);
  1239. gs_vertex2f(0.0f + (thickness / scale.x), 1.0f);
  1240. gs_vertex2f(0.0f, 1.0f);
  1241. gs_vertex2f(0.0f, 0.0f);
  1242. gs_vertex2f(0.0f, 1.0f);
  1243. gs_vertex2f(0.0f, 1.0f - (thickness / scale.y));
  1244. gs_vertex2f(1.0f, 1.0f - (thickness / scale.y));
  1245. gs_vertex2f(1.0f, 1.0f);
  1246. gs_vertex2f(0.0f, 1.0f);
  1247. gs_vertex2f(1.0f, 1.0f);
  1248. gs_vertex2f(1.0f - (thickness / scale.x), 1.0f);
  1249. gs_vertex2f(1.0f - (thickness / scale.x), 0.0f);
  1250. gs_vertex2f(1.0f, 0.0f);
  1251. gs_vertex2f(1.0f, 1.0f);
  1252. gs_vertex2f(1.0f, 0.0f);
  1253. gs_vertex2f(1.0f, 0.0f + (thickness / scale.y));
  1254. gs_vertex2f(0.0f, 0.0f + (thickness / scale.y));
  1255. gs_vertex2f(0.0f, 0.0f);
  1256. gs_vertex2f(1.0f, 0.0f);
  1257. gs_vertbuffer_t *rect = gs_render_save();
  1258. gs_load_vertexbuffer(rect);
  1259. gs_draw(GS_TRISTRIP, 0, 0);
  1260. gs_vertexbuffer_destroy(rect);
  1261. }
  1262. static inline bool crop_enabled(const obs_sceneitem_crop *crop)
  1263. {
  1264. return crop->left > 0 || crop->top > 0 || crop->right > 0 ||
  1265. crop->bottom > 0;
  1266. }
  1267. bool OBSBasicPreview::DrawSelectedOverflow(obs_scene_t *scene,
  1268. obs_sceneitem_t *item, void *param)
  1269. {
  1270. if (obs_sceneitem_locked(item))
  1271. return true;
  1272. if (!SceneItemHasVideo(item))
  1273. return true;
  1274. bool select = config_get_bool(GetGlobalConfig(), "BasicWindow",
  1275. "OverflowSelectionHidden");
  1276. if (!select && !obs_sceneitem_visible(item))
  1277. return true;
  1278. if (obs_sceneitem_is_group(item)) {
  1279. matrix4 mat;
  1280. obs_sceneitem_get_draw_transform(item, &mat);
  1281. gs_matrix_push();
  1282. gs_matrix_mul(&mat);
  1283. obs_sceneitem_group_enum_items(item, DrawSelectedOverflow,
  1284. param);
  1285. gs_matrix_pop();
  1286. }
  1287. bool always = config_get_bool(GetGlobalConfig(), "BasicWindow",
  1288. "OverflowAlwaysVisible");
  1289. if (!always && !obs_sceneitem_selected(item))
  1290. return true;
  1291. OBSBasicPreview *prev = reinterpret_cast<OBSBasicPreview *>(param);
  1292. matrix4 boxTransform;
  1293. matrix4 invBoxTransform;
  1294. obs_sceneitem_get_box_transform(item, &boxTransform);
  1295. matrix4_inv(&invBoxTransform, &boxTransform);
  1296. vec3 bounds[] = {
  1297. {{{0.f, 0.f, 0.f}}},
  1298. {{{1.f, 0.f, 0.f}}},
  1299. {{{0.f, 1.f, 0.f}}},
  1300. {{{1.f, 1.f, 0.f}}},
  1301. };
  1302. bool visible = std::all_of(
  1303. std::begin(bounds), std::end(bounds), [&](const vec3 &b) {
  1304. vec3 pos;
  1305. vec3_transform(&pos, &b, &boxTransform);
  1306. vec3_transform(&pos, &pos, &invBoxTransform);
  1307. return CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);
  1308. });
  1309. if (!visible)
  1310. return true;
  1311. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedOverflow");
  1312. obs_transform_info info;
  1313. obs_sceneitem_get_info(item, &info);
  1314. gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_REPEAT);
  1315. gs_eparam_t *image = gs_effect_get_param_by_name(solid, "image");
  1316. gs_eparam_t *scale = gs_effect_get_param_by_name(solid, "scale");
  1317. vec2 s;
  1318. vec2_set(&s, boxTransform.x.x / 96, boxTransform.y.y / 96);
  1319. gs_effect_set_vec2(scale, &s);
  1320. gs_effect_set_texture(image, prev->overflow);
  1321. gs_matrix_push();
  1322. gs_matrix_mul(&boxTransform);
  1323. obs_sceneitem_crop crop;
  1324. obs_sceneitem_get_crop(item, &crop);
  1325. while (gs_effect_loop(solid, "Draw")) {
  1326. gs_draw_sprite(prev->overflow, 0, 1, 1);
  1327. }
  1328. gs_matrix_pop();
  1329. GS_DEBUG_MARKER_END();
  1330. UNUSED_PARAMETER(scene);
  1331. return true;
  1332. }
  1333. bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
  1334. obs_sceneitem_t *item, void *param)
  1335. {
  1336. if (obs_sceneitem_locked(item))
  1337. return true;
  1338. if (!SceneItemHasVideo(item))
  1339. return true;
  1340. if (obs_sceneitem_is_group(item)) {
  1341. matrix4 mat;
  1342. obs_sceneitem_get_draw_transform(item, &mat);
  1343. gs_matrix_push();
  1344. gs_matrix_mul(&mat);
  1345. obs_sceneitem_group_enum_items(item, DrawSelectedItem, param);
  1346. gs_matrix_pop();
  1347. }
  1348. OBSBasicPreview *prev = reinterpret_cast<OBSBasicPreview *>(param);
  1349. OBSBasic *main = OBSBasic::Get();
  1350. bool hovered = false;
  1351. {
  1352. std::lock_guard<std::mutex> lock(prev->selectMutex);
  1353. for (int i = 0; i < prev->hoveredPreviewItems.size(); i++) {
  1354. if (prev->hoveredPreviewItems[i] == item) {
  1355. hovered = true;
  1356. break;
  1357. }
  1358. }
  1359. }
  1360. bool selected = obs_sceneitem_selected(item);
  1361. if (!selected && !hovered)
  1362. return true;
  1363. matrix4 boxTransform;
  1364. matrix4 invBoxTransform;
  1365. obs_sceneitem_get_box_transform(item, &boxTransform);
  1366. matrix4_inv(&invBoxTransform, &boxTransform);
  1367. vec3 bounds[] = {
  1368. {{{0.f, 0.f, 0.f}}},
  1369. {{{1.f, 0.f, 0.f}}},
  1370. {{{0.f, 1.f, 0.f}}},
  1371. {{{1.f, 1.f, 0.f}}},
  1372. };
  1373. vec4 red;
  1374. vec4 green;
  1375. vec4 blue;
  1376. vec4_set(&red, 1.0f, 0.0f, 0.0f, 1.0f);
  1377. vec4_set(&green, 0.0f, 1.0f, 0.0f, 1.0f);
  1378. vec4_set(&blue, 0.0f, 0.5f, 1.0f, 1.0f);
  1379. bool visible = std::all_of(
  1380. std::begin(bounds), std::end(bounds), [&](const vec3 &b) {
  1381. vec3 pos;
  1382. vec3_transform(&pos, &b, &boxTransform);
  1383. vec3_transform(&pos, &pos, &invBoxTransform);
  1384. return CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);
  1385. });
  1386. if (!visible)
  1387. return true;
  1388. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedItem");
  1389. matrix4 curTransform;
  1390. vec2 boxScale;
  1391. gs_matrix_get(&curTransform);
  1392. obs_sceneitem_get_box_scale(item, &boxScale);
  1393. boxScale.x *= curTransform.x.x;
  1394. boxScale.y *= curTransform.y.y;
  1395. obs_transform_info info;
  1396. obs_sceneitem_get_info(item, &info);
  1397. gs_matrix_push();
  1398. gs_matrix_mul(&boxTransform);
  1399. obs_sceneitem_crop crop;
  1400. obs_sceneitem_get_crop(item, &crop);
  1401. gs_effect_t *eff = gs_get_effect();
  1402. gs_eparam_t *colParam = gs_effect_get_param_by_name(eff, "color");
  1403. if (info.bounds_type == OBS_BOUNDS_NONE && crop_enabled(&crop)) {
  1404. #define DRAW_SIDE(side, x1, y1, x2, y2) \
  1405. if (hovered && !selected) \
  1406. gs_effect_set_vec4(colParam, &blue); \
  1407. else if (crop.side > 0) \
  1408. gs_effect_set_vec4(colParam, &green); \
  1409. DrawLine(x1, y1, x2, y2, HANDLE_RADIUS / 2, boxScale); \
  1410. gs_effect_set_vec4(colParam, &red);
  1411. DRAW_SIDE(left, 0.0f, 0.0f, 0.0f, 1.0f);
  1412. DRAW_SIDE(top, 0.0f, 0.0f, 1.0f, 0.0f);
  1413. DRAW_SIDE(right, 1.0f, 0.0f, 1.0f, 1.0f);
  1414. DRAW_SIDE(bottom, 0.0f, 1.0f, 1.0f, 1.0f);
  1415. #undef DRAW_SIDE
  1416. } else {
  1417. if (!selected) {
  1418. gs_effect_set_vec4(colParam, &blue);
  1419. DrawRect(HANDLE_RADIUS / 2, boxScale);
  1420. } else {
  1421. DrawRect(HANDLE_RADIUS / 2, boxScale);
  1422. }
  1423. }
  1424. gs_load_vertexbuffer(main->box);
  1425. gs_effect_set_vec4(colParam, &red);
  1426. if (selected) {
  1427. DrawSquareAtPos(0.0f, 0.0f);
  1428. DrawSquareAtPos(0.0f, 1.0f);
  1429. DrawSquareAtPos(1.0f, 0.0f);
  1430. DrawSquareAtPos(1.0f, 1.0f);
  1431. DrawSquareAtPos(0.5f, 0.0f);
  1432. DrawSquareAtPos(0.0f, 0.5f);
  1433. DrawSquareAtPos(0.5f, 1.0f);
  1434. DrawSquareAtPos(1.0f, 0.5f);
  1435. }
  1436. gs_matrix_pop();
  1437. GS_DEBUG_MARKER_END();
  1438. UNUSED_PARAMETER(scene);
  1439. UNUSED_PARAMETER(param);
  1440. return true;
  1441. }
  1442. bool OBSBasicPreview::DrawSelectionBox(float x1, float y1, float x2, float y2,
  1443. gs_vertbuffer_t *rectFill)
  1444. {
  1445. x1 = std::round(x1);
  1446. x2 = std::round(x2);
  1447. y1 = std::round(y1);
  1448. y2 = std::round(y2);
  1449. gs_effect_t *eff = gs_get_effect();
  1450. gs_eparam_t *colParam = gs_effect_get_param_by_name(eff, "color");
  1451. vec4 fillColor;
  1452. vec4_set(&fillColor, 0.7f, 0.7f, 0.7f, 0.5f);
  1453. vec4 borderColor;
  1454. vec4_set(&borderColor, 1.0f, 1.0f, 1.0f, 1.0f);
  1455. vec2 scale;
  1456. vec2_set(&scale, std::abs(x2 - x1), std::abs(y2 - y1));
  1457. gs_matrix_push();
  1458. gs_matrix_identity();
  1459. gs_matrix_translate3f(x1, y1, 0.0f);
  1460. gs_matrix_scale3f(x2 - x1, y2 - y1, 1.0f);
  1461. gs_effect_set_vec4(colParam, &fillColor);
  1462. gs_load_vertexbuffer(rectFill);
  1463. gs_draw(GS_TRISTRIP, 0, 0);
  1464. gs_effect_set_vec4(colParam, &borderColor);
  1465. DrawRect(HANDLE_RADIUS / 2, scale);
  1466. gs_matrix_pop();
  1467. return true;
  1468. }
  1469. void OBSBasicPreview::DrawOverflow()
  1470. {
  1471. if (locked)
  1472. return;
  1473. bool hidden = config_get_bool(GetGlobalConfig(), "BasicWindow",
  1474. "OverflowHidden");
  1475. if (hidden)
  1476. return;
  1477. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawOverflow");
  1478. if (!overflow) {
  1479. std::string path;
  1480. GetDataFilePath("images/overflow.png", path);
  1481. overflow = gs_texture_create_from_file(path.c_str());
  1482. }
  1483. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  1484. OBSScene scene = main->GetCurrentScene();
  1485. if (scene) {
  1486. gs_matrix_push();
  1487. gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
  1488. obs_scene_enum_items(scene, DrawSelectedOverflow, this);
  1489. gs_matrix_pop();
  1490. }
  1491. gs_load_vertexbuffer(nullptr);
  1492. GS_DEBUG_MARKER_END();
  1493. }
  1494. void OBSBasicPreview::DrawSceneEditing()
  1495. {
  1496. if (locked)
  1497. return;
  1498. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSceneEditing");
  1499. OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
  1500. gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
  1501. gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
  1502. vec4 color;
  1503. vec4_set(&color, 1.0f, 0.0f, 0.0f, 1.0f);
  1504. gs_effect_set_vec4(gs_effect_get_param_by_name(solid, "color"), &color);
  1505. gs_technique_begin(tech);
  1506. gs_technique_begin_pass(tech, 0);
  1507. OBSScene scene = main->GetCurrentScene();
  1508. if (scene) {
  1509. gs_matrix_push();
  1510. gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
  1511. obs_scene_enum_items(scene, DrawSelectedItem, this);
  1512. gs_matrix_pop();
  1513. }
  1514. if (selectionBox) {
  1515. if (!rectFill) {
  1516. gs_render_start(true);
  1517. gs_vertex2f(0.0f, 0.0f);
  1518. gs_vertex2f(1.0f, 0.0f);
  1519. gs_vertex2f(1.0f, 1.0f);
  1520. gs_vertex2f(1.0f, 1.0f);
  1521. gs_vertex2f(0.0f, 0.0f);
  1522. gs_vertex2f(0.0f, 1.0f);
  1523. rectFill = gs_render_save();
  1524. }
  1525. DrawSelectionBox(startPos.x * main->previewScale,
  1526. startPos.y * main->previewScale,
  1527. mousePos.x * main->previewScale,
  1528. mousePos.y * main->previewScale, rectFill);
  1529. }
  1530. gs_load_vertexbuffer(nullptr);
  1531. gs_technique_end_pass(tech);
  1532. gs_technique_end(tech);
  1533. GS_DEBUG_MARKER_END();
  1534. }
  1535. void OBSBasicPreview::ResetScrollingOffset()
  1536. {
  1537. vec2_zero(&scrollingOffset);
  1538. }
  1539. void OBSBasicPreview::SetScalingLevel(int32_t newScalingLevelVal)
  1540. {
  1541. float newScalingAmountVal =
  1542. pow(ZOOM_SENSITIVITY, float(newScalingLevelVal));
  1543. scalingLevel = newScalingLevelVal;
  1544. SetScalingAmount(newScalingAmountVal);
  1545. }
  1546. void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal)
  1547. {
  1548. scrollingOffset.x *= newScalingAmountVal / scalingAmount;
  1549. scrollingOffset.y *= newScalingAmountVal / scalingAmount;
  1550. scalingAmount = newScalingAmountVal;
  1551. }
  1552. OBSBasicPreview *OBSBasicPreview::Get()
  1553. {
  1554. return OBSBasic::Get()->ui->preview;
  1555. }