| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610 | 
							- #include <QGuiApplication>
 
- #include <QMouseEvent>
 
- #include <algorithm>
 
- #include <cmath>
 
- #include <string>
 
- #include <graphics/vec4.h>
 
- #include <graphics/matrix4.h>
 
- #include "window-basic-preview.hpp"
 
- #include "window-basic-main.hpp"
 
- #include "obs-app.hpp"
 
- #include "platform.hpp"
 
- #define HANDLE_RADIUS     4.0f
 
- #define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f)
 
- #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
 
- #define SUPPORTS_FRACTIONAL_SCALING
 
- #endif
 
- /* TODO: make C++ math classes and clean up code here later */
 
- OBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags)
 
- 	: OBSQTDisplay(parent, flags)
 
- {
 
- 	ResetScrollingOffset();
 
- 	setMouseTracking(true);
 
- }
 
- OBSBasicPreview::~OBSBasicPreview()
 
- {
 
- 	if (overflow) {
 
- 		obs_enter_graphics();
 
- 		gs_texture_destroy(overflow);
 
- 		obs_leave_graphics();
 
- 	}
 
- }
 
- vec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- #ifdef SUPPORTS_FRACTIONAL_SCALING
 
- 	float pixelRatio = main->devicePixelRatioF();
 
- #else
 
- 	float pixelRatio = main->devicePixelRatio();
 
- #endif
 
- 	float scale = pixelRatio / main->previewScale;
 
- 	vec2 pos;
 
- 	vec2_set(&pos,
 
- 		(float(event->x()) - main->previewX / pixelRatio) * scale,
 
- 		(float(event->y()) - main->previewY / pixelRatio) * scale);
 
- 	return pos;
 
- }
 
- struct SceneFindData {
 
- 	const vec2   &pos;
 
- 	OBSSceneItem item;
 
- 	bool         selectBelow;
 
- 	obs_sceneitem_t *group = nullptr;
 
- 	SceneFindData(const SceneFindData &) = delete;
 
- 	SceneFindData(SceneFindData &&) = delete;
 
- 	SceneFindData& operator=(const SceneFindData &) = delete;
 
- 	SceneFindData& operator=(SceneFindData &&) = delete;
 
- 	inline SceneFindData(const vec2 &pos_, bool selectBelow_)
 
- 		: pos         (pos_),
 
- 		  selectBelow (selectBelow_)
 
- 	{}
 
- };
 
- static bool SceneItemHasVideo(obs_sceneitem_t *item)
 
- {
 
- 	obs_source_t *source = obs_sceneitem_get_source(item);
 
- 	uint32_t flags = obs_source_get_output_flags(source);
 
- 	return (flags & OBS_SOURCE_VIDEO) != 0;
 
- }
 
- static bool CloseFloat(float a, float b, float epsilon=0.01)
 
- {
 
- 	using std::abs;
 
- 	return abs(a-b) <= epsilon;
 
- }
 
- static bool FindItemAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
 
- 		void *param)
 
- {
 
- 	SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
 
- 	matrix4       transform;
 
- 	matrix4       invTransform;
 
- 	vec3          transformedPos;
 
- 	vec3          pos3;
 
- 	vec3          pos3_;
 
- 	if (!SceneItemHasVideo(item))
 
- 		return true;
 
- 	if (obs_sceneitem_locked(item))
 
- 		return true;
 
- 	vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
 
- 	obs_sceneitem_get_box_transform(item, &transform);
 
- 	matrix4_inv(&invTransform, &transform);
 
- 	vec3_transform(&transformedPos, &pos3, &invTransform);
 
- 	vec3_transform(&pos3_, &transformedPos, &transform);
 
- 	if (CloseFloat(pos3.x, pos3_.x) && CloseFloat(pos3.y, pos3_.y) &&
 
- 	    transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
 
- 	    transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
 
- 		if (data->selectBelow && obs_sceneitem_selected(item)) {
 
- 			if (data->item)
 
- 				return false;
 
- 			else
 
- 				data->selectBelow = false;
 
- 		}
 
- 		data->item = item;
 
- 	}
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- static vec3 GetTransformedPos(float x, float y, const matrix4 &mat)
 
- {
 
- 	vec3 result;
 
- 	vec3_set(&result, x, y, 0.0f);
 
- 	vec3_transform(&result, &result, &mat);
 
- 	return result;
 
- }
 
- static inline vec2 GetOBSScreenSize()
 
- {
 
- 	obs_video_info ovi;
 
- 	vec2 size;
 
- 	vec2_zero(&size);
 
- 	if (obs_get_video_info(&ovi)) {
 
- 		size.x = float(ovi.base_width);
 
- 		size.y = float(ovi.base_height);
 
- 	}
 
- 	return size;
 
- }
 
- vec3 OBSBasicPreview::GetSnapOffset(const vec3 &tl, const vec3 &br)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	vec2 screenSize = GetOBSScreenSize();
 
- 	vec3 clampOffset;
 
- 	vec3_zero(&clampOffset);
 
- 	const bool snap = config_get_bool(GetGlobalConfig(),
 
- 			"BasicWindow", "SnappingEnabled");
 
- 	if (snap == false)
 
- 		return clampOffset;
 
- 	const bool screenSnap = config_get_bool(GetGlobalConfig(),
 
- 			"BasicWindow", "ScreenSnapping");
 
- 	const bool centerSnap = config_get_bool(GetGlobalConfig(),
 
- 			"BasicWindow", "CenterSnapping");
 
- 	const float clampDist = config_get_double(GetGlobalConfig(),
 
- 			"BasicWindow", "SnapDistance") / main->previewScale;
 
- 	const float centerX = br.x - (br.x - tl.x) / 2.0f;
 
- 	const float centerY = br.y - (br.y - tl.y) / 2.0f;
 
- 	// Left screen edge.
 
- 	if (screenSnap &&
 
- 	    fabsf(tl.x) < clampDist)
 
- 		clampOffset.x = -tl.x;
 
- 	// Right screen edge.
 
- 	if (screenSnap &&
 
- 	    fabsf(clampOffset.x) < EPSILON &&
 
- 	    fabsf(screenSize.x - br.x) < clampDist)
 
- 		clampOffset.x = screenSize.x - br.x;
 
- 	// Horizontal center.
 
- 	if (centerSnap &&
 
- 	    fabsf(screenSize.x - (br.x - tl.x)) > clampDist &&
 
- 	    fabsf(screenSize.x / 2.0f - centerX) < clampDist)
 
- 		clampOffset.x = screenSize.x / 2.0f - centerX;
 
- 	// Top screen edge.
 
- 	if (screenSnap &&
 
- 	    fabsf(tl.y) < clampDist)
 
- 		clampOffset.y = -tl.y;
 
- 	// Bottom screen edge.
 
- 	if (screenSnap &&
 
- 	    fabsf(clampOffset.y) < EPSILON &&
 
- 	    fabsf(screenSize.y - br.y) < clampDist)
 
- 		clampOffset.y = screenSize.y - br.y;
 
- 	// Vertical center.
 
- 	if (centerSnap &&
 
- 	    fabsf(screenSize.y - (br.y - tl.y)) > clampDist &&
 
- 	    fabsf(screenSize.y / 2.0f - centerY) < clampDist)
 
- 		clampOffset.y = screenSize.y / 2.0f - centerY;
 
- 	return clampOffset;
 
- }
 
- OBSSceneItem OBSBasicPreview::GetItemAtPos(const vec2 &pos, bool selectBelow)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	if (!scene)
 
- 		return OBSSceneItem();
 
- 	SceneFindData data(pos, selectBelow);
 
- 	obs_scene_enum_items(scene, FindItemAtPos, &data);
 
- 	return data.item;
 
- }
 
- static bool CheckItemSelected(obs_scene_t *scene, obs_sceneitem_t *item,
 
- 		void *param)
 
- {
 
- 	SceneFindData *data = reinterpret_cast<SceneFindData*>(param);
 
- 	matrix4       transform;
 
- 	vec3          transformedPos;
 
- 	vec3          pos3;
 
- 	if (!SceneItemHasVideo(item))
 
- 		return true;
 
- 	if (obs_sceneitem_is_group(item)) {
 
- 		data->group = item;
 
- 		obs_sceneitem_group_enum_items(item, CheckItemSelected, param);
 
- 		data->group = nullptr;
 
- 		if (data->item) {
 
- 			return false;
 
- 		}
 
- 	}
 
- 	vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
 
- 	obs_sceneitem_get_box_transform(item, &transform);
 
- 	if (data->group) {
 
- 		matrix4 parent_transform;
 
- 		obs_sceneitem_get_draw_transform(data->group, &parent_transform);
 
- 		matrix4_mul(&transform, &transform, &parent_transform);
 
- 	}
 
- 	matrix4_inv(&transform, &transform);
 
- 	vec3_transform(&transformedPos, &pos3, &transform);
 
- 	if (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f &&
 
- 	    transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {
 
- 		if (obs_sceneitem_selected(item)) {
 
- 			data->item = item;
 
- 			return false;
 
- 		}
 
- 	}
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- bool OBSBasicPreview::SelectedAtPos(const vec2 &pos)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	if (!scene)
 
- 		return false;
 
- 	SceneFindData data(pos, false);
 
- 	obs_scene_enum_items(scene, CheckItemSelected, &data);
 
- 	return !!data.item;
 
- }
 
- struct HandleFindData {
 
- 	const vec2    &pos;
 
- 	const float   radius;
 
- 	matrix4       parent_xform;
 
- 	OBSSceneItem item;
 
- 	ItemHandle   handle = ItemHandle::None;
 
- 	HandleFindData(const HandleFindData &) = delete;
 
- 	HandleFindData(HandleFindData &&) = delete;
 
- 	HandleFindData& operator=(const HandleFindData &) = delete;
 
- 	HandleFindData& operator=(HandleFindData &&) = delete;
 
- 	inline HandleFindData(const vec2 &pos_, float scale)
 
- 		: pos    (pos_),
 
- 		  radius (HANDLE_SEL_RADIUS / scale)
 
- 	{
 
- 		matrix4_identity(&parent_xform);
 
- 	}
 
- 	inline HandleFindData(const HandleFindData &hfd,
 
- 			obs_sceneitem_t *parent)
 
- 		: pos    (hfd.pos),
 
- 		  radius (hfd.radius),
 
- 		  item   (hfd.item),
 
- 		  handle (hfd.handle)
 
- 	{
 
- 		obs_sceneitem_get_draw_transform(parent, &parent_xform);
 
- 	}
 
- };
 
- static bool FindHandleAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
 
- 		void *param)
 
- {
 
- 	HandleFindData &data = *reinterpret_cast<HandleFindData*>(param);
 
- 	if (!obs_sceneitem_selected(item)) {
 
- 		if (obs_sceneitem_is_group(item)) {
 
- 			HandleFindData newData(data, item);
 
- 			obs_sceneitem_group_enum_items(item, FindHandleAtPos,
 
- 					&newData);
 
- 			data.item = newData.item;
 
- 			data.handle = newData.handle;
 
- 		}
 
- 		return true;
 
- 	}
 
- 	matrix4        transform;
 
- 	vec3           pos3;
 
- 	float          closestHandle = data.radius;
 
- 	vec3_set(&pos3, data.pos.x, data.pos.y, 0.0f);
 
- 	obs_sceneitem_get_box_transform(item, &transform);
 
- 	auto TestHandle = [&] (float x, float y, ItemHandle handle)
 
- 	{
 
- 		vec3 handlePos = GetTransformedPos(x, y, transform);
 
- 		vec3_transform(&handlePos, &handlePos, &data.parent_xform);
 
- 		float dist = vec3_dist(&handlePos, &pos3);
 
- 		if (dist < data.radius) {
 
- 			if (dist < closestHandle) {
 
- 				closestHandle = dist;
 
- 				data.handle   = handle;
 
- 				data.item     = item;
 
- 			}
 
- 		}
 
- 	};
 
- 	TestHandle(0.0f, 0.0f, ItemHandle::TopLeft);
 
- 	TestHandle(0.5f, 0.0f, ItemHandle::TopCenter);
 
- 	TestHandle(1.0f, 0.0f, ItemHandle::TopRight);
 
- 	TestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);
 
- 	TestHandle(1.0f, 0.5f, ItemHandle::CenterRight);
 
- 	TestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);
 
- 	TestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);
 
- 	TestHandle(1.0f, 1.0f, ItemHandle::BottomRight);
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- static vec2 GetItemSize(obs_sceneitem_t *item)
 
- {
 
- 	obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);
 
- 	vec2 size;
 
- 	if (boundsType != OBS_BOUNDS_NONE) {
 
- 		obs_sceneitem_get_bounds(item, &size);
 
- 	} else {
 
- 		obs_source_t *source = obs_sceneitem_get_source(item);
 
- 		obs_sceneitem_crop crop;
 
- 		vec2 scale;
 
- 		obs_sceneitem_get_scale(item, &scale);
 
- 		obs_sceneitem_get_crop(item, &crop);
 
- 		size.x = float(obs_source_get_width(source) -
 
- 				crop.left - crop.right) * scale.x;
 
- 		size.y = float(obs_source_get_height(source) -
 
- 				crop.top - crop.bottom) * scale.y;
 
- 	}
 
- 	return size;
 
- }
 
- void OBSBasicPreview::GetStretchHandleData(const vec2 &pos)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	if (!scene)
 
- 		return;
 
- #ifdef SUPPORTS_FRACTIONAL_SCALING
 
- 	float scale = main->previewScale / main->devicePixelRatioF();
 
- #else
 
- 	float scale = main->previewScale / main->devicePixelRatio();
 
- #endif
 
- 	vec2 scaled_pos = pos;
 
- 	vec2_divf(&scaled_pos, &scaled_pos, scale);
 
- 	HandleFindData data(scaled_pos, scale);
 
- 	obs_scene_enum_items(scene, FindHandleAtPos, &data);
 
- 	stretchItem     = std::move(data.item);
 
- 	stretchHandle   = data.handle;
 
- 	if (stretchHandle != ItemHandle::None) {
 
- 		matrix4 boxTransform;
 
- 		vec3    itemUL;
 
- 		float   itemRot;
 
- 		stretchItemSize = GetItemSize(stretchItem);
 
- 		obs_sceneitem_get_box_transform(stretchItem, &boxTransform);
 
- 		itemRot = obs_sceneitem_get_rot(stretchItem);
 
- 		vec3_from_vec4(&itemUL, &boxTransform.t);
 
- 		/* build the item space conversion matrices */
 
- 		matrix4_identity(&itemToScreen);
 
- 		matrix4_rotate_aa4f(&itemToScreen, &itemToScreen,
 
- 				0.0f, 0.0f, 1.0f, RAD(itemRot));
 
- 		matrix4_translate3f(&itemToScreen, &itemToScreen,
 
- 				itemUL.x, itemUL.y, 0.0f);
 
- 		matrix4_identity(&screenToItem);
 
- 		matrix4_translate3f(&screenToItem, &screenToItem,
 
- 				-itemUL.x, -itemUL.y, 0.0f);
 
- 		matrix4_rotate_aa4f(&screenToItem, &screenToItem,
 
- 				0.0f, 0.0f, 1.0f, RAD(-itemRot));
 
- 		obs_sceneitem_get_crop(stretchItem, &startCrop);
 
- 		obs_sceneitem_get_pos(stretchItem, &startItemPos);
 
- 		obs_source_t *source = obs_sceneitem_get_source(stretchItem);
 
- 		cropSize.x = float(obs_source_get_width(source) -
 
- 				startCrop.left - startCrop.right);
 
- 		cropSize.y = float(obs_source_get_height(source) -
 
- 				startCrop.top - startCrop.bottom);
 
- 		stretchGroup = obs_sceneitem_get_group(scene, stretchItem);
 
- 		if (stretchGroup) {
 
- 			obs_sceneitem_get_draw_transform(stretchGroup,
 
- 					&invGroupTransform);
 
- 			matrix4_inv(&invGroupTransform,
 
- 					&invGroupTransform);
 
- 			obs_sceneitem_defer_group_resize_begin(stretchGroup);
 
- 		}
 
- 	}
 
- }
 
- void OBSBasicPreview::keyPressEvent(QKeyEvent *event)
 
- {
 
- 	if (!IsFixedScaling() || event->isAutoRepeat()) {
 
- 		OBSQTDisplay::keyPressEvent(event);
 
- 		return;
 
- 	}
 
- 	switch (event->key()) {
 
- 	case Qt::Key_Space:
 
- 		setCursor(Qt::OpenHandCursor);
 
- 		scrollMode = true;
 
- 		break;
 
- 	}
 
- 	OBSQTDisplay::keyPressEvent(event);
 
- }
 
- void OBSBasicPreview::keyReleaseEvent(QKeyEvent *event)
 
- {
 
- 	if (event->isAutoRepeat()) {
 
- 		OBSQTDisplay::keyReleaseEvent(event);
 
- 		return;
 
- 	}
 
- 	switch (event->key()) {
 
- 	case Qt::Key_Space:
 
- 		scrollMode = false;
 
- 		setCursor(Qt::ArrowCursor);
 
- 		break;
 
- 	}
 
- 	OBSQTDisplay::keyReleaseEvent(event);
 
- }
 
- void OBSBasicPreview::wheelEvent(QWheelEvent *event)
 
- {
 
- 	if (scrollMode && IsFixedScaling()
 
- 			&& event->orientation() == Qt::Vertical) {
 
- 		if (event->delta() > 0)
 
- 			SetScalingLevel(scalingLevel + 1);
 
- 		else if (event->delta() < 0)
 
- 			SetScalingLevel(scalingLevel - 1);
 
- 		emit DisplayResized();
 
- 	}
 
- 	OBSQTDisplay::wheelEvent(event);
 
- }
 
- void OBSBasicPreview::mousePressEvent(QMouseEvent *event)
 
- {
 
- 	if (scrollMode && IsFixedScaling() &&
 
- 	    event->button() == Qt::LeftButton) {
 
- 		setCursor(Qt::ClosedHandCursor);
 
- 		scrollingFrom.x = event->x();
 
- 		scrollingFrom.y = event->y();
 
- 		return;
 
- 	}
 
- 	if (event->button() == Qt::RightButton) {
 
- 		scrollMode = false;
 
- 		setCursor(Qt::ArrowCursor);
 
- 	}
 
- 	if (locked) {
 
- 		OBSQTDisplay::mousePressEvent(event);
 
- 		return;
 
- 	}
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- #ifdef SUPPORTS_FRACTIONAL_SCALING
 
- 	float pixelRatio = main->devicePixelRatioF();
 
- #else
 
- 	float pixelRatio = main->devicePixelRatio();
 
- #endif
 
- 	float x = float(event->x()) - main->previewX / pixelRatio;
 
- 	float y = float(event->y()) - main->previewY / pixelRatio;
 
- 	Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
 
- 	bool altDown = (modifiers & Qt::AltModifier);
 
- 	OBSQTDisplay::mousePressEvent(event);
 
- 	if (event->button() != Qt::LeftButton &&
 
- 	    event->button() != Qt::RightButton)
 
- 		return;
 
- 	if (event->button() == Qt::LeftButton)
 
- 		mouseDown = true;
 
- 	if (altDown)
 
- 		cropping = true;
 
- 	vec2_set(&startPos, x, y);
 
- 	GetStretchHandleData(startPos);
 
- 	vec2_divf(&startPos, &startPos, main->previewScale / pixelRatio);
 
- 	startPos.x = std::round(startPos.x);
 
- 	startPos.y = std::round(startPos.y);
 
- 	mouseOverItems = SelectedAtPos(startPos);
 
- 	vec2_zero(&lastMoveOffset);
 
- }
 
- static bool select_one(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
 
- {
 
- 	obs_sceneitem_t *selectedItem =
 
- 		reinterpret_cast<obs_sceneitem_t*>(param);
 
- 	if (obs_sceneitem_is_group(item))
 
- 		obs_sceneitem_group_enum_items(item, select_one, param);
 
- 	obs_sceneitem_select(item, (selectedItem == item));
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- void OBSBasicPreview::DoSelect(const vec2 &pos)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene     scene = main->GetCurrentScene();
 
- 	OBSSceneItem item  = GetItemAtPos(pos, true);
 
- 	obs_scene_enum_items(scene, select_one, (obs_sceneitem_t*)item);
 
- }
 
- void OBSBasicPreview::DoCtrlSelect(const vec2 &pos)
 
- {
 
- 	OBSSceneItem item = GetItemAtPos(pos, false);
 
- 	if (!item)
 
- 		return;
 
- 	bool selected = obs_sceneitem_selected(item);
 
- 	obs_sceneitem_select(item, !selected);
 
- }
 
- void OBSBasicPreview::ProcessClick(const vec2 &pos)
 
- {
 
- 	Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
 
- 	if (modifiers & Qt::ControlModifier)
 
- 		DoCtrlSelect(pos);
 
- 	else
 
- 		DoSelect(pos);
 
- }
 
- void OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)
 
- {
 
- 	if (scrollMode)
 
- 		setCursor(Qt::OpenHandCursor);
 
- 	if (locked) {
 
- 		OBSQTDisplay::mouseReleaseEvent(event);
 
- 		return;
 
- 	}
 
- 	if (mouseDown) {
 
- 		vec2 pos = GetMouseEventPos(event);
 
- 		if (!mouseMoved)
 
- 			ProcessClick(pos);
 
- 		if (stretchGroup) {
 
- 			obs_sceneitem_defer_group_resize_end(stretchGroup);
 
- 		}
 
- 		stretchItem  = nullptr;
 
- 		stretchGroup = nullptr;
 
- 		mouseDown    = false;
 
- 		mouseMoved   = false;
 
- 		cropping     = false;
 
- 		OBSSceneItem item = GetItemAtPos(pos, true);
 
- 		hoveredPreviewItem = item;
 
- 	}
 
- }
 
- struct SelectedItemBounds {
 
- 	bool first = true;
 
- 	vec3 tl, br;
 
- };
 
- static bool AddItemBounds(obs_scene_t *scene, obs_sceneitem_t *item,
 
- 		void *param)
 
- {
 
- 	SelectedItemBounds *data = reinterpret_cast<SelectedItemBounds*>(param);
 
- 	vec3 t[4];
 
- 	auto add_bounds = [data, &t] ()
 
- 	{
 
- 		for (const vec3 &v : t) {
 
- 			if (data->first) {
 
- 				vec3_copy(&data->tl, &v);
 
- 				vec3_copy(&data->br, &v);
 
- 				data->first = false;
 
- 			} else {
 
- 				vec3_min(&data->tl, &data->tl, &v);
 
- 				vec3_max(&data->br, &data->br, &v);
 
- 			}
 
- 		}
 
- 	};
 
- 	if (obs_sceneitem_is_group(item)) {
 
- 		SelectedItemBounds sib;
 
- 		obs_sceneitem_group_enum_items(item, AddItemBounds, &sib);
 
- 		if (!sib.first) {
 
- 			matrix4 xform;
 
- 			obs_sceneitem_get_draw_transform(item, &xform);
 
- 			vec3_set(&t[0], sib.tl.x, sib.tl.y, 0.0f);
 
- 			vec3_set(&t[1], sib.tl.x, sib.br.y, 0.0f);
 
- 			vec3_set(&t[2], sib.br.x, sib.tl.y, 0.0f);
 
- 			vec3_set(&t[3], sib.br.x, sib.br.y, 0.0f);
 
- 			vec3_transform(&t[0], &t[0], &xform);
 
- 			vec3_transform(&t[1], &t[1], &xform);
 
- 			vec3_transform(&t[2], &t[2], &xform);
 
- 			vec3_transform(&t[3], &t[3], &xform);
 
- 			add_bounds();
 
- 		}
 
- 	}
 
- 	if (!obs_sceneitem_selected(item))
 
- 		return true;
 
- 	matrix4 boxTransform;
 
- 	obs_sceneitem_get_box_transform(item, &boxTransform);
 
- 	t[0] = GetTransformedPos(0.0f, 0.0f, boxTransform);
 
- 	t[1] = GetTransformedPos(1.0f, 0.0f, boxTransform);
 
- 	t[2] = GetTransformedPos(0.0f, 1.0f, boxTransform);
 
- 	t[3] = GetTransformedPos(1.0f, 1.0f, boxTransform);
 
- 	add_bounds();
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- struct OffsetData {
 
- 	float clampDist;
 
- 	vec3 tl, br, offset;
 
- };
 
- static bool GetSourceSnapOffset(obs_scene_t *scene, obs_sceneitem_t *item,
 
- 		void *param)
 
- {
 
- 	OffsetData *data = reinterpret_cast<OffsetData*>(param);
 
- 	if (obs_sceneitem_selected(item))
 
- 		return true;
 
- 	matrix4 boxTransform;
 
- 	obs_sceneitem_get_box_transform(item, &boxTransform);
 
- 	vec3 t[4] = {
 
- 		GetTransformedPos(0.0f, 0.0f, boxTransform),
 
- 		GetTransformedPos(1.0f, 0.0f, boxTransform),
 
- 		GetTransformedPos(0.0f, 1.0f, boxTransform),
 
- 		GetTransformedPos(1.0f, 1.0f, boxTransform)
 
- 	};
 
- 	bool first = true;
 
- 	vec3 tl, br;
 
- 	vec3_zero(&tl);
 
- 	vec3_zero(&br);
 
- 	for (const vec3 &v : t) {
 
- 		if (first) {
 
- 			vec3_copy(&tl, &v);
 
- 			vec3_copy(&br, &v);
 
- 			first = false;
 
- 		} else {
 
- 			vec3_min(&tl, &tl, &v);
 
- 			vec3_max(&br, &br, &v);
 
- 		}
 
- 	}
 
- 	// Snap to other source edges
 
- #define EDGE_SNAP(l, r, x, y) \
 
- 	do { \
 
- 		double dist = fabsf(l.x - data->r.x); \
 
- 		if (dist < data->clampDist && \
 
- 		    fabsf(data->offset.x) < EPSILON && \
 
- 		    data->tl.y < br.y && \
 
- 		    data->br.y > tl.y && \
 
- 		    (fabsf(data->offset.x) > dist || data->offset.x < EPSILON)) \
 
- 			data->offset.x = l.x - data->r.x; \
 
- 	} while (false)
 
- 	EDGE_SNAP(tl, br, x, y);
 
- 	EDGE_SNAP(tl, br, y, x);
 
- 	EDGE_SNAP(br, tl, x, y);
 
- 	EDGE_SNAP(br, tl, y, x);
 
- #undef EDGE_SNAP
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- void OBSBasicPreview::SnapItemMovement(vec2 &offset)
 
- {
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	SelectedItemBounds data;
 
- 	obs_scene_enum_items(scene, AddItemBounds, &data);
 
- 	data.tl.x += offset.x;
 
- 	data.tl.y += offset.y;
 
- 	data.br.x += offset.x;
 
- 	data.br.y += offset.y;
 
- 	vec3 snapOffset = GetSnapOffset(data.tl, data.br);
 
- 	const bool snap = config_get_bool(GetGlobalConfig(),
 
- 			"BasicWindow", "SnappingEnabled");
 
- 	const bool sourcesSnap = config_get_bool(GetGlobalConfig(),
 
- 			"BasicWindow", "SourceSnapping");
 
- 	if (snap == false)
 
- 		return;
 
- 	if (sourcesSnap == false) {
 
- 		offset.x += snapOffset.x;
 
- 		offset.y += snapOffset.y;
 
- 		return;
 
- 	}
 
- 	const float clampDist = config_get_double(GetGlobalConfig(),
 
- 			"BasicWindow", "SnapDistance") / main->previewScale;
 
- 	OffsetData offsetData;
 
- 	offsetData.clampDist = clampDist;
 
- 	offsetData.tl = data.tl;
 
- 	offsetData.br = data.br;
 
- 	vec3_copy(&offsetData.offset, &snapOffset);
 
- 	obs_scene_enum_items(scene, GetSourceSnapOffset, &offsetData);
 
- 	if (fabsf(offsetData.offset.x) > EPSILON ||
 
- 	    fabsf(offsetData.offset.y) > EPSILON) {
 
- 		offset.x += offsetData.offset.x;
 
- 		offset.y += offsetData.offset.y;
 
- 	} else {
 
- 		offset.x += snapOffset.x;
 
- 		offset.y += snapOffset.y;
 
- 	}
 
- }
 
- static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
 
- {
 
- 	if (obs_sceneitem_locked(item))
 
- 		return true;
 
- 	bool selected = obs_sceneitem_selected(item);
 
- 	vec2 *offset = reinterpret_cast<vec2*>(param);
 
- 	if (obs_sceneitem_is_group(item) && !selected) {
 
- 		matrix4 transform;
 
- 		vec3 new_offset;
 
- 		vec3_set(&new_offset, offset->x, offset->y, 0.0f);
 
- 		obs_sceneitem_get_draw_transform(item, &transform);
 
- 		vec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);
 
- 		matrix4_inv(&transform, &transform);
 
- 		vec3_transform(&new_offset, &new_offset, &transform);
 
- 		obs_sceneitem_group_enum_items(item, move_items, &new_offset);
 
- 	}
 
- 	if (selected) {
 
- 		vec2 pos;
 
- 		obs_sceneitem_get_pos(item, &pos);
 
- 		vec2_add(&pos, &pos, offset);
 
- 		obs_sceneitem_set_pos(item, &pos);
 
- 	}
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- void OBSBasicPreview::MoveItems(const vec2 &pos)
 
- {
 
- 	Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	vec2 offset, moveOffset;
 
- 	vec2_sub(&offset, &pos, &startPos);
 
- 	vec2_sub(&moveOffset, &offset, &lastMoveOffset);
 
- 	if (!(modifiers & Qt::ControlModifier))
 
- 		SnapItemMovement(moveOffset);
 
- 	vec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset);
 
- 	obs_scene_enum_items(scene, move_items, &moveOffset);
 
- }
 
- vec3 OBSBasicPreview::CalculateStretchPos(const vec3 &tl, const vec3 &br)
 
- {
 
- 	uint32_t alignment = obs_sceneitem_get_alignment(stretchItem);
 
- 	vec3 pos;
 
- 	vec3_zero(&pos);
 
- 	if (alignment & OBS_ALIGN_LEFT)
 
- 		pos.x = tl.x;
 
- 	else if (alignment & OBS_ALIGN_RIGHT)
 
- 		pos.x = br.x;
 
- 	else
 
- 		pos.x = (br.x - tl.x) * 0.5f + tl.x;
 
- 	if (alignment & OBS_ALIGN_TOP)
 
- 		pos.y = tl.y;
 
- 	else if (alignment & OBS_ALIGN_BOTTOM)
 
- 		pos.y = br.y;
 
- 	else
 
- 		pos.y = (br.y - tl.y) * 0.5f + tl.y;
 
- 	return pos;
 
- }
 
- void OBSBasicPreview::ClampAspect(vec3 &tl, vec3 &br, vec2 &size,
 
- 		const vec2 &baseSize)
 
- {
 
- 	float    baseAspect   = baseSize.x / baseSize.y;
 
- 	float    aspect       = size.x / size.y;
 
- 	uint32_t stretchFlags = (uint32_t)stretchHandle;
 
- 	if (stretchHandle == ItemHandle::TopLeft    ||
 
- 	    stretchHandle == ItemHandle::TopRight   ||
 
- 	    stretchHandle == ItemHandle::BottomLeft ||
 
- 	    stretchHandle == ItemHandle::BottomRight) {
 
- 		if (aspect < baseAspect) {
 
- 			if ((size.y >= 0.0f && size.x >= 0.0f) ||
 
- 			    (size.y <= 0.0f && size.x <= 0.0f))
 
- 				size.x = size.y * baseAspect;
 
- 			else
 
- 				size.x = size.y * baseAspect * -1.0f;
 
- 		} else {
 
- 			if ((size.y >= 0.0f && size.x >= 0.0f) ||
 
- 			    (size.y <= 0.0f && size.x <= 0.0f))
 
- 				size.y = size.x / baseAspect;
 
- 			else
 
- 				size.y = size.x / baseAspect * -1.0f;
 
- 		}
 
- 	} else if (stretchHandle == ItemHandle::TopCenter ||
 
- 	           stretchHandle == ItemHandle::BottomCenter) {
 
- 		if ((size.y >= 0.0f && size.x >= 0.0f) ||
 
- 		    (size.y <= 0.0f && size.x <= 0.0f))
 
- 			size.x = size.y * baseAspect;
 
- 		else
 
- 			size.x = size.y * baseAspect * -1.0f;
 
- 	} else if (stretchHandle == ItemHandle::CenterLeft ||
 
- 	           stretchHandle == ItemHandle::CenterRight) {
 
- 		if ((size.y >= 0.0f && size.x >= 0.0f) ||
 
- 		    (size.y <= 0.0f && size.x <= 0.0f))
 
- 			size.y = size.x / baseAspect;
 
- 		else
 
- 			size.y = size.x / baseAspect * -1.0f;
 
- 	}
 
- 	size.x = std::round(size.x);
 
- 	size.y = std::round(size.y);
 
- 	if (stretchFlags & ITEM_LEFT)
 
- 		tl.x = br.x - size.x;
 
- 	else if (stretchFlags & ITEM_RIGHT)
 
- 		br.x = tl.x + size.x;
 
- 	if (stretchFlags & ITEM_TOP)
 
- 		tl.y = br.y - size.y;
 
- 	else if (stretchFlags & ITEM_BOTTOM)
 
- 		br.y = tl.y + size.y;
 
- }
 
- void OBSBasicPreview::SnapStretchingToScreen(vec3 &tl, vec3 &br)
 
- {
 
- 	uint32_t stretchFlags = (uint32_t)stretchHandle;
 
- 	vec3     newTL        = GetTransformedPos(tl.x, tl.y, itemToScreen);
 
- 	vec3     newTR        = GetTransformedPos(br.x, tl.y, itemToScreen);
 
- 	vec3     newBL        = GetTransformedPos(tl.x, br.y, itemToScreen);
 
- 	vec3     newBR        = GetTransformedPos(br.x, br.y, itemToScreen);
 
- 	vec3     boundingTL;
 
- 	vec3     boundingBR;
 
- 	vec3_copy(&boundingTL, &newTL);
 
- 	vec3_min(&boundingTL, &boundingTL, &newTR);
 
- 	vec3_min(&boundingTL, &boundingTL, &newBL);
 
- 	vec3_min(&boundingTL, &boundingTL, &newBR);
 
- 	vec3_copy(&boundingBR, &newTL);
 
- 	vec3_max(&boundingBR, &boundingBR, &newTR);
 
- 	vec3_max(&boundingBR, &boundingBR, &newBL);
 
- 	vec3_max(&boundingBR, &boundingBR, &newBR);
 
- 	vec3 offset = GetSnapOffset(boundingTL, boundingBR);
 
- 	vec3_add(&offset, &offset, &newTL);
 
- 	vec3_transform(&offset, &offset, &screenToItem);
 
- 	vec3_sub(&offset, &offset, &tl);
 
- 	if (stretchFlags & ITEM_LEFT)
 
- 		tl.x += offset.x;
 
- 	else if (stretchFlags & ITEM_RIGHT)
 
- 		br.x += offset.x;
 
- 	if (stretchFlags & ITEM_TOP)
 
- 		tl.y += offset.y;
 
- 	else if (stretchFlags & ITEM_BOTTOM)
 
- 		br.y += offset.y;
 
- }
 
- static float maxfunc(float x, float y)
 
- {
 
- 	return x > y ? x : y;
 
- }
 
- static float minfunc(float x, float y)
 
- {
 
- 	return x < y ? x : y;
 
- }
 
- void OBSBasicPreview::CropItem(const vec2 &pos)
 
- {
 
- 	obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
 
- 	uint32_t stretchFlags = (uint32_t)stretchHandle;
 
- 	uint32_t align = obs_sceneitem_get_alignment(stretchItem);
 
- 	vec3 tl, br, pos3;
 
- 	vec3_zero(&tl);
 
- 	vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
 
- 	vec3_set(&pos3, pos.x, pos.y, 0.0f);
 
- 	vec3_transform(&pos3, &pos3, &screenToItem);
 
- 	obs_sceneitem_crop crop = startCrop;
 
- 	vec2 scale;
 
- 	obs_sceneitem_get_scale(stretchItem, &scale);
 
- 	vec2 max_tl;
 
- 	vec2 max_br;
 
- 	vec2_set(&max_tl,
 
- 		float(-crop.left) * scale.x,
 
- 		float(-crop.top) * scale.y);
 
- 	vec2_set(&max_br,
 
- 		stretchItemSize.x + crop.right * scale.x,
 
- 		stretchItemSize.y + crop.bottom * scale.y);
 
- 	typedef std::function<float (float, float)> minmax_func_t;
 
- 	minmax_func_t min_x = scale.x < 0.0f ? maxfunc : minfunc;
 
- 	minmax_func_t min_y = scale.y < 0.0f ? maxfunc : minfunc;
 
- 	minmax_func_t max_x = scale.x < 0.0f ? minfunc : maxfunc;
 
- 	minmax_func_t max_y = scale.y < 0.0f ? minfunc : maxfunc;
 
- 	pos3.x = min_x(pos3.x, max_br.x);
 
- 	pos3.x = max_x(pos3.x, max_tl.x);
 
- 	pos3.y = min_y(pos3.y, max_br.y);
 
- 	pos3.y = max_y(pos3.y, max_tl.y);
 
- 	if (stretchFlags & ITEM_LEFT) {
 
- 		float maxX = stretchItemSize.x - (2.0 * scale.x);
 
- 		pos3.x = tl.x = min_x(pos3.x, maxX);
 
- 	} else if (stretchFlags & ITEM_RIGHT) {
 
- 		float minX = (2.0 * scale.x);
 
- 		pos3.x = br.x = max_x(pos3.x, minX);
 
- 	}
 
- 	if (stretchFlags & ITEM_TOP) {
 
- 		float maxY = stretchItemSize.y - (2.0 * scale.y);
 
- 		pos3.y = tl.y = min_y(pos3.y, maxY);
 
- 	} else if (stretchFlags & ITEM_BOTTOM) {
 
- 		float minY = (2.0 * scale.y);
 
- 		pos3.y = br.y = max_y(pos3.y, minY);
 
- 	}
 
- #define ALIGN_X (ITEM_LEFT|ITEM_RIGHT)
 
- #define ALIGN_Y (ITEM_TOP|ITEM_BOTTOM)
 
- 	vec3 newPos;
 
- 	vec3_zero(&newPos);
 
- 	uint32_t align_x = (align & ALIGN_X);
 
- 	uint32_t align_y = (align & ALIGN_Y);
 
- 	if (align_x == (stretchFlags & ALIGN_X) && align_x != 0)
 
- 		newPos.x = pos3.x;
 
- 	else if (align & ITEM_RIGHT)
 
- 		newPos.x = stretchItemSize.x;
 
- 	else if (!(align & ITEM_LEFT))
 
- 		newPos.x = stretchItemSize.x * 0.5f;
 
- 	if (align_y == (stretchFlags & ALIGN_Y) && align_y != 0)
 
- 		newPos.y = pos3.y;
 
- 	else if (align & ITEM_BOTTOM)
 
- 		newPos.y = stretchItemSize.y;
 
- 	else if (!(align & ITEM_TOP))
 
- 		newPos.y = stretchItemSize.y * 0.5f;
 
- #undef ALIGN_X
 
- #undef ALIGN_Y
 
- 	crop = startCrop;
 
- 	if (stretchFlags & ITEM_LEFT)
 
- 		crop.left += int(std::round(tl.x / scale.x));
 
- 	else if (stretchFlags & ITEM_RIGHT)
 
- 		crop.right += int(std::round((stretchItemSize.x - br.x) / scale.x));
 
- 	if (stretchFlags & ITEM_TOP)
 
- 		crop.top += int(std::round(tl.y / scale.y));
 
- 	else if (stretchFlags & ITEM_BOTTOM)
 
- 		crop.bottom += int(std::round((stretchItemSize.y - br.y) / scale.y));
 
- 	vec3_transform(&newPos, &newPos, &itemToScreen);
 
- 	newPos.x = std::round(newPos.x);
 
- 	newPos.y = std::round(newPos.y);
 
- #if 0
 
- 	vec3 curPos;
 
- 	vec3_zero(&curPos);
 
- 	obs_sceneitem_get_pos(stretchItem, (vec2*)&curPos);
 
- 	blog(LOG_DEBUG, "curPos {%d, %d} - newPos {%d, %d}",
 
- 			int(curPos.x), int(curPos.y),
 
- 			int(newPos.x), int(newPos.y));
 
- 	blog(LOG_DEBUG, "crop {%d, %d, %d, %d}",
 
- 			crop.left, crop.top,
 
- 			crop.right, crop.bottom);
 
- #endif
 
- 	obs_sceneitem_defer_update_begin(stretchItem);
 
- 	obs_sceneitem_set_crop(stretchItem, &crop);
 
- 	if (boundsType == OBS_BOUNDS_NONE)
 
- 		obs_sceneitem_set_pos(stretchItem, (vec2*)&newPos);
 
- 	obs_sceneitem_defer_update_end(stretchItem);
 
- }
 
- void OBSBasicPreview::StretchItem(const vec2 &pos)
 
- {
 
- 	Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
 
- 	obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);
 
- 	uint32_t stretchFlags = (uint32_t)stretchHandle;
 
- 	bool shiftDown = (modifiers & Qt::ShiftModifier);
 
- 	vec3 tl, br, pos3;
 
- 	vec3_zero(&tl);
 
- 	vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);
 
- 	vec3_set(&pos3, pos.x, pos.y, 0.0f);
 
- 	vec3_transform(&pos3, &pos3, &screenToItem);
 
- 	if (stretchFlags & ITEM_LEFT)
 
- 		tl.x = pos3.x;
 
- 	else if (stretchFlags & ITEM_RIGHT)
 
- 		br.x = pos3.x;
 
- 	if (stretchFlags & ITEM_TOP)
 
- 		tl.y = pos3.y;
 
- 	else if (stretchFlags & ITEM_BOTTOM)
 
- 		br.y = pos3.y;
 
- 	if (!(modifiers & Qt::ControlModifier))
 
- 		SnapStretchingToScreen(tl, br);
 
- 	obs_source_t *source = obs_sceneitem_get_source(stretchItem);
 
- 	vec2 baseSize;
 
- 	vec2_set(&baseSize,
 
- 		float(obs_source_get_width(source)),
 
- 		float(obs_source_get_height(source)));
 
- 	vec2 size;
 
- 	vec2_set(&size,br. x - tl.x, br.y - tl.y);
 
- 	if (boundsType != OBS_BOUNDS_NONE) {
 
- 		if (shiftDown)
 
- 			ClampAspect(tl, br, size, baseSize);
 
- 		if (tl.x > br.x) std::swap(tl.x, br.x);
 
- 		if (tl.y > br.y) std::swap(tl.y, br.y);
 
- 		vec2_abs(&size, &size);
 
- 		obs_sceneitem_set_bounds(stretchItem, &size);
 
- 	} else {
 
- 		obs_sceneitem_crop crop;
 
- 		obs_sceneitem_get_crop(stretchItem, &crop);
 
- 		baseSize.x -= float(crop.left + crop.right);
 
- 		baseSize.y -= float(crop.top + crop.bottom);
 
- 		if (!shiftDown)
 
- 			ClampAspect(tl, br, size, baseSize);
 
- 		vec2_div(&size, &size, &baseSize);
 
- 		obs_sceneitem_set_scale(stretchItem, &size);
 
- 	}
 
- 	pos3 = CalculateStretchPos(tl, br);
 
- 	vec3_transform(&pos3, &pos3, &itemToScreen);
 
- 	vec2 newPos;
 
- 	vec2_set(&newPos, std::round(pos3.x), std::round(pos3.y));
 
- 	obs_sceneitem_set_pos(stretchItem, &newPos);
 
- }
 
- void OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)
 
- {
 
- 	if (scrollMode && event->buttons() == Qt::LeftButton) {
 
- 		scrollingOffset.x += event->x() - scrollingFrom.x;
 
- 		scrollingOffset.y += event->y() - scrollingFrom.y;
 
- 		scrollingFrom.x = event->x();
 
- 		scrollingFrom.y = event->y();
 
- 		emit DisplayResized();
 
- 		return;
 
- 	}
 
- 	if (locked)
 
- 		return;
 
- 	if (mouseDown) {
 
- 		hoveredPreviewItem = nullptr;
 
- 		vec2 pos = GetMouseEventPos(event);
 
- 		if (!mouseMoved && !mouseOverItems &&
 
- 		    stretchHandle == ItemHandle::None) {
 
- 			ProcessClick(startPos);
 
- 			mouseOverItems = SelectedAtPos(startPos);
 
- 		}
 
- 		pos.x = std::round(pos.x);
 
- 		pos.y = std::round(pos.y);
 
- 		if (stretchHandle != ItemHandle::None) {
 
- 			OBSBasic *main = reinterpret_cast<OBSBasic*>(
 
- 					App()->GetMainWindow());
 
- 			OBSScene scene = main->GetCurrentScene();
 
- 			obs_sceneitem_t *group = obs_sceneitem_get_group(
 
- 					scene, stretchItem);
 
- 			if (group) {
 
- 				vec3 group_pos;
 
- 				vec3_set(&group_pos, pos.x, pos.y, 0.0f);
 
- 				vec3_transform(&group_pos, &group_pos,
 
- 						&invGroupTransform);
 
- 				pos.x = group_pos.x;
 
- 				pos.y = group_pos.y;
 
- 			}
 
- 			if (cropping)
 
- 				CropItem(pos);
 
- 			else
 
- 				StretchItem(pos);
 
- 		} else if (mouseOverItems) {
 
- 			MoveItems(pos);
 
- 		}
 
- 		mouseMoved = true;
 
- 	} else {
 
- 		vec2 pos = GetMouseEventPos(event);
 
- 		OBSSceneItem item = GetItemAtPos(pos, true);
 
- 		hoveredPreviewItem = item;
 
- 	}
 
- }
 
- void OBSBasicPreview::leaveEvent(QEvent *event)
 
- {
 
- 	hoveredPreviewItem = nullptr;
 
- 	UNUSED_PARAMETER(event);
 
- }
 
- static void DrawSquareAtPos(float x, float y)
 
- {
 
- 	struct vec3 pos;
 
- 	vec3_set(&pos, x, y, 0.0f);
 
- 	struct matrix4 matrix;
 
- 	gs_matrix_get(&matrix);
 
- 	vec3_transform(&pos, &pos, &matrix);
 
- 	gs_matrix_push();
 
- 	gs_matrix_identity();
 
- 	gs_matrix_translate(&pos);
 
- 	gs_matrix_translate3f(-HANDLE_RADIUS, -HANDLE_RADIUS, 0.0f);
 
- 	gs_matrix_scale3f(HANDLE_RADIUS*2, HANDLE_RADIUS*2, 1.0f);
 
- 	gs_draw(GS_TRISTRIP, 0, 0);
 
- 	gs_matrix_pop();
 
- }
 
- static void DrawLine(float x1, float y1, float x2, float y2, float thickness,
 
- 		vec2 scale)
 
- {
 
- 	float ySide = (y1 == y2) ? (y1 < 0.5f ? 1.0f : -1.0f) : 0.0f;
 
- 	float xSide = (x1 == x2) ? (x1 < 0.5f ? 1.0f : -1.0f) : 0.0f;
 
- 	gs_render_start(true);
 
- 	gs_vertex2f(x1, y1);
 
- 	gs_vertex2f(x1 + (xSide * (thickness / scale.x)),
 
- 		y1 + (ySide * (thickness / scale.y)));
 
- 	gs_vertex2f(x2 + (xSide * (thickness / scale.x)),
 
- 		y2 + (ySide * (thickness / scale.y)));
 
- 	gs_vertex2f(x2, y2);
 
- 	gs_vertex2f(x1, y1);
 
- 	gs_vertbuffer_t *line = gs_render_save();
 
- 	gs_load_vertexbuffer(line);
 
- 	gs_draw(GS_TRISTRIP, 0, 0);
 
- 	gs_vertexbuffer_destroy(line);
 
- }
 
- static void DrawRect(float thickness, vec2 scale)
 
- {
 
- 	gs_render_start(true);
 
- 	gs_vertex2f(0.0f, 0.0f);
 
- 	gs_vertex2f(0.0f + (thickness / scale.x), 0.0f);
 
- 	gs_vertex2f(0.0f + (thickness / scale.x), 1.0f);
 
- 	gs_vertex2f(0.0f, 1.0f);
 
- 	gs_vertex2f(0.0f, 0.0f);
 
- 	gs_vertex2f(0.0f, 1.0f);
 
- 	gs_vertex2f(0.0f, 1.0f - (thickness / scale.y));
 
- 	gs_vertex2f(1.0f, 1.0f - (thickness / scale.y));
 
- 	gs_vertex2f(1.0f, 1.0f);
 
- 	gs_vertex2f(0.0f, 1.0f);
 
- 	gs_vertex2f(1.0f, 1.0f);
 
- 	gs_vertex2f(1.0f - (thickness / scale.x), 1.0f);
 
- 	gs_vertex2f(1.0f - (thickness / scale.x), 0.0f);
 
- 	gs_vertex2f(1.0f, 0.0f);
 
- 	gs_vertex2f(1.0f, 1.0f);
 
- 	gs_vertex2f(1.0f, 0.0f);
 
- 	gs_vertex2f(1.0f, 0.0f + (thickness / scale.y));
 
- 	gs_vertex2f(0.0f, 0.0f + (thickness / scale.y));
 
- 	gs_vertex2f(0.0f, 0.0f);
 
- 	gs_vertex2f(1.0f, 0.0f);
 
- 	gs_vertbuffer_t *rect = gs_render_save();
 
- 	gs_load_vertexbuffer(rect);
 
- 	gs_draw(GS_TRISTRIP, 0, 0);
 
- 	gs_vertexbuffer_destroy(rect);
 
- }
 
- static inline bool crop_enabled(const obs_sceneitem_crop *crop)
 
- {
 
- 	return crop->left > 0  ||
 
- 	       crop->top > 0   ||
 
- 	       crop->right > 0 ||
 
- 	       crop->bottom > 0;
 
- }
 
- bool OBSBasicPreview::DrawSelectedOverflow(obs_scene_t *scene,
 
- 	obs_sceneitem_t *item, void *param)
 
- {
 
- 	if (obs_sceneitem_locked(item))
 
- 		return true;
 
- 	if (!SceneItemHasVideo(item))
 
- 		return true;
 
- 	bool select = config_get_bool(GetGlobalConfig(), "BasicWindow",
 
- 		"OverflowSelectionHidden");
 
- 	if (!select && !obs_sceneitem_visible(item))
 
- 		return true;
 
- 	if (obs_sceneitem_is_group(item)) {
 
- 		matrix4 mat;
 
- 		obs_sceneitem_get_draw_transform(item, &mat);
 
- 		gs_matrix_push();
 
- 		gs_matrix_mul(&mat);
 
- 		obs_sceneitem_group_enum_items(item, DrawSelectedOverflow, param);
 
- 		gs_matrix_pop();
 
- 	}
 
- 	bool always = config_get_bool(GetGlobalConfig(), "BasicWindow",
 
- 		"OverflowAlwaysVisible");
 
- 	if (!always && !obs_sceneitem_selected(item))
 
- 		return true;
 
- 	OBSBasicPreview *prev = reinterpret_cast<OBSBasicPreview*>(param);
 
- 	matrix4 boxTransform;
 
- 	matrix4 invBoxTransform;
 
- 	obs_sceneitem_get_box_transform(item, &boxTransform);
 
- 	matrix4_inv(&invBoxTransform, &boxTransform);
 
- 	vec3 bounds[] = {
 
- 		{{{0.f, 0.f, 0.f}}},
 
- 		{{{1.f, 0.f, 0.f}}},
 
- 		{{{0.f, 1.f, 0.f}}},
 
- 		{{{1.f, 1.f, 0.f}}},
 
- 	};
 
- 	bool visible = std::all_of(std::begin(bounds), std::end(bounds),
 
- 		[&](const vec3 &b)
 
- 	{
 
- 		vec3 pos;
 
- 		vec3_transform(&pos, &b, &boxTransform);
 
- 		vec3_transform(&pos, &pos, &invBoxTransform);
 
- 		return CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);
 
- 	});
 
- 	if (!visible)
 
- 		return true;
 
- 	GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedOverflow");
 
- 	obs_transform_info info;
 
- 	obs_sceneitem_get_info(item, &info);
 
- 	gs_effect_t    *solid = obs_get_base_effect(OBS_EFFECT_REPEAT);
 
- 	gs_eparam_t    *image = gs_effect_get_param_by_name(solid, "image");
 
- 	gs_eparam_t    *scale = gs_effect_get_param_by_name(solid, "scale");
 
- 	vec2 s;
 
- 	vec2_set(&s, boxTransform.x.x / 96, boxTransform.y.y / 96);
 
- 	gs_effect_set_vec2(scale, &s);
 
- 	gs_effect_set_texture(image, prev->overflow);
 
- 	gs_matrix_push();
 
- 	gs_matrix_mul(&boxTransform);
 
- 	obs_sceneitem_crop crop;
 
- 	obs_sceneitem_get_crop(item, &crop);
 
- 	while (gs_effect_loop(solid, "Draw")) {
 
- 		gs_draw_sprite(prev->overflow, 0, 1, 1);
 
- 	}
 
- 	gs_matrix_pop();
 
- 	GS_DEBUG_MARKER_END();
 
- 	UNUSED_PARAMETER(scene);
 
- 	return true;
 
- }
 
- bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
 
- 		obs_sceneitem_t *item, void *param)
 
- {
 
- 	if (obs_sceneitem_locked(item))
 
- 		return true;
 
- 	if (!SceneItemHasVideo(item))
 
- 		return true;
 
- 	if (obs_sceneitem_is_group(item)) {
 
- 		matrix4 mat;
 
- 		obs_sceneitem_get_draw_transform(item, &mat);
 
- 		gs_matrix_push();
 
- 		gs_matrix_mul(&mat);
 
- 		obs_sceneitem_group_enum_items(item, DrawSelectedItem, param);
 
- 		gs_matrix_pop();
 
- 	}
 
- 	OBSBasicPreview *prev = reinterpret_cast<OBSBasicPreview*>(param);
 
- 	OBSBasic *main = OBSBasic::Get();
 
- 	bool hovered = prev->hoveredPreviewItem == item ||
 
- 	               prev->hoveredListItem == item;
 
- 	bool selected = obs_sceneitem_selected(item);
 
- 	if (!selected && !hovered)
 
- 		return true;
 
- 	matrix4 boxTransform;
 
- 	matrix4 invBoxTransform;
 
- 	obs_sceneitem_get_box_transform(item, &boxTransform);
 
- 	matrix4_inv(&invBoxTransform, &boxTransform);
 
- 	vec3 bounds[] = {
 
- 		{{{0.f, 0.f, 0.f}}},
 
- 		{{{1.f, 0.f, 0.f}}},
 
- 		{{{0.f, 1.f, 0.f}}},
 
- 		{{{1.f, 1.f, 0.f}}},
 
- 	};
 
- 	vec4 red;
 
- 	vec4 green;
 
- 	vec4 blue;
 
- 	vec4_set(&red, 1.0f, 0.0f, 0.0f, 1.0f);
 
- 	vec4_set(&green, 0.0f, 1.0f, 0.0f, 1.0f);
 
- 	vec4_set(&blue, 0.0f, 0.5f, 1.0f, 1.0f);
 
- 	bool visible = std::all_of(std::begin(bounds), std::end(bounds),
 
- 			[&](const vec3 &b)
 
- 	{
 
- 		vec3 pos;
 
- 		vec3_transform(&pos, &b, &boxTransform);
 
- 		vec3_transform(&pos, &pos, &invBoxTransform);
 
- 		return CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);
 
- 	});
 
- 	if (!visible)
 
- 		return true;
 
- 	GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSelectedItem");
 
- 	matrix4 curTransform;
 
- 	vec2 boxScale;
 
- 	gs_matrix_get(&curTransform);
 
- 	obs_sceneitem_get_box_scale(item, &boxScale);
 
- 	boxScale.x *= curTransform.x.x;
 
- 	boxScale.y *= curTransform.y.y;
 
- 	obs_transform_info info;
 
- 	obs_sceneitem_get_info(item, &info);
 
- 	gs_matrix_push();
 
- 	gs_matrix_mul(&boxTransform);
 
- 	obs_sceneitem_crop crop;
 
- 	obs_sceneitem_get_crop(item, &crop);
 
- 	gs_effect_t *eff = gs_get_effect();
 
- 	gs_eparam_t *colParam = gs_effect_get_param_by_name(eff, "color");
 
- 	if (info.bounds_type == OBS_BOUNDS_NONE && crop_enabled(&crop)) {
 
- #define DRAW_SIDE(side, x1, y1, x2, y2) \
 
- 		if (hovered && !selected) \
 
- 			gs_effect_set_vec4(colParam, &blue); \
 
- 		else if (crop.side > 0) \
 
- 			gs_effect_set_vec4(colParam, &green); \
 
- 		DrawLine(x1, y1, x2, y2, HANDLE_RADIUS / 2, boxScale); \
 
- 		gs_effect_set_vec4(colParam, &red);
 
- 		DRAW_SIDE(left,   0.0f, 0.0f, 0.0f, 1.0f);
 
- 		DRAW_SIDE(top,    0.0f, 0.0f, 1.0f, 0.0f);
 
- 		DRAW_SIDE(right,  1.0f, 0.0f, 1.0f, 1.0f);
 
- 		DRAW_SIDE(bottom, 0.0f, 1.0f, 1.0f, 1.0f);
 
- #undef DRAW_SIDE
 
- 	} else {
 
- 		if (!selected) {
 
- 			gs_effect_set_vec4(colParam, &blue);
 
- 			DrawRect(HANDLE_RADIUS / 2, boxScale);
 
- 		} else {
 
- 			DrawRect(HANDLE_RADIUS / 2, boxScale);
 
- 		}
 
- 	}
 
- 	gs_load_vertexbuffer(main->box);
 
- 	gs_effect_set_vec4(colParam, &red);
 
- 	if (selected) {
 
- 		DrawSquareAtPos(0.0f, 0.0f);
 
- 		DrawSquareAtPos(0.0f, 1.0f);
 
- 		DrawSquareAtPos(1.0f, 0.0f);
 
- 		DrawSquareAtPos(1.0f, 1.0f);
 
- 		DrawSquareAtPos(0.5f, 0.0f);
 
- 		DrawSquareAtPos(0.0f, 0.5f);
 
- 		DrawSquareAtPos(0.5f, 1.0f);
 
- 		DrawSquareAtPos(1.0f, 0.5f);
 
- 	}
 
- 	gs_matrix_pop();
 
- 	GS_DEBUG_MARKER_END();
 
- 	UNUSED_PARAMETER(scene);
 
- 	UNUSED_PARAMETER(param);
 
- 	return true;
 
- }
 
- void OBSBasicPreview::DrawOverflow()
 
- {
 
- 	if (locked)
 
- 		return;
 
- 	bool hidden = config_get_bool(GetGlobalConfig(), "BasicWindow",
 
- 		"OverflowHidden");
 
- 	if (hidden)
 
- 		return;
 
- 	GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawOverflow");
 
- 	if (!overflow) {
 
- 		std::string path;
 
- 		GetDataFilePath("images/overflow.png", path);
 
- 		overflow = gs_texture_create_from_file(path.c_str());
 
- 	}
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	if (scene) {
 
- 		gs_matrix_push();
 
- 		gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
 
- 		obs_scene_enum_items(scene, DrawSelectedOverflow, this);
 
- 		gs_matrix_pop();
 
- 	}
 
- 	gs_load_vertexbuffer(nullptr);
 
- 	GS_DEBUG_MARKER_END();
 
- }
 
- void OBSBasicPreview::DrawSceneEditing()
 
- {
 
- 	if (locked)
 
- 		return;
 
- 	GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawSceneEditing");
 
- 	OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
 
- 	gs_effect_t    *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
 
- 	gs_technique_t *tech  = gs_effect_get_technique(solid, "Solid");
 
- 	vec4 color;
 
- 	vec4_set(&color, 1.0f, 0.0f, 0.0f, 1.0f);
 
- 	gs_effect_set_vec4(gs_effect_get_param_by_name(solid, "color"), &color);
 
- 	gs_technique_begin(tech);
 
- 	gs_technique_begin_pass(tech, 0);
 
- 	OBSScene scene = main->GetCurrentScene();
 
- 	if (scene) {
 
- 		gs_matrix_push();
 
- 		gs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);
 
- 		obs_scene_enum_items(scene, DrawSelectedItem, this);
 
- 		gs_matrix_pop();
 
- 	}
 
- 	gs_load_vertexbuffer(nullptr);
 
- 	gs_technique_end_pass(tech);
 
- 	gs_technique_end(tech);
 
- 	GS_DEBUG_MARKER_END();
 
- }
 
- void OBSBasicPreview::ResetScrollingOffset()
 
- {
 
- 	vec2_zero(&scrollingOffset);
 
- }
 
- void OBSBasicPreview::SetScalingLevel(int32_t newScalingLevelVal) {
 
- 	float newScalingAmountVal = pow(ZOOM_SENSITIVITY, float(newScalingLevelVal));
 
- 	scalingLevel = newScalingLevelVal;
 
- 	SetScalingAmount(newScalingAmountVal);
 
- }
 
- void OBSBasicPreview::SetScalingAmount(float newScalingAmountVal) {
 
- 	scrollingOffset.x *= newScalingAmountVal / scalingAmount;
 
- 	scrollingOffset.y *= newScalingAmountVal / scalingAmount;
 
- 	scalingAmount = newScalingAmountVal;
 
- }
 
- OBSBasicPreview *OBSBasicPreview::Get()
 
- {
 
- 	return OBSBasic::Get()->ui->preview;
 
- }
 
 
  |