Kaynağa Gözat

UI: Add option to snap to edges of other sources

Allows the user to snap sources to the edges of other sources, on by
default.

Closes jp9000/obs-studio#530
Jeremiah Senkpiel 9 yıl önce
ebeveyn
işleme
7463c0dbaa

+ 1 - 0
obs/data/locale/en-US.ini

@@ -358,6 +358,7 @@ Basic.Settings.General.WarnBeforeStoppingStream="Show confirmation dialog when s
 Basic.Settings.General.Snapping="Source Alignment Snapping"
 Basic.Settings.General.ScreenSnapping="Snap Sources to edge of screen"
 Basic.Settings.General.CenterSnapping="Snap Sources to horizontal and vertical center"
+Basic.Settings.General.SourceSnapping="Snap Sources to other sources"
 Basic.Settings.General.SnapDistance="Snap Sensitivity"
 
 # basic mode 'stream' settings

+ 26 - 0
obs/forms/OBSBasicSettings.ui

@@ -243,6 +243,16 @@
               </property>
              </widget>
             </item>
+            <item row="3" column="1">
+             <widget class="QCheckBox" name="sourceSnapping">
+              <property name="text">
+               <string>Basic.Settings.General.SourceSnapping</string>
+              </property>
+              <property name="checked">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
             <item row="1" column="1">
              <widget class="QDoubleSpinBox" name="snapDistance">
               <property name="decimals">
@@ -3626,6 +3636,22 @@
      <x>476</x>
      <y>202</y>
     </hint>
+    <hint type="destinationlabel">
+     <x>466</x>
+     <y>253</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>snappingEnabled</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>sourceSnapping</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>518</x>
+     <y>204</y>
+    </hint>
     <hint type="destinationlabel">
      <x>515</x>
      <y>277</y>

+ 2 - 0
obs/obs-app.cpp

@@ -343,6 +343,8 @@ bool OBSApp::InitGlobalConfigDefaults()
 			"SnappingEnabled", true);
 	config_set_default_bool(globalConfig, "BasicWindow",
 			"ScreenSnapping", true);
+	config_set_default_bool(globalConfig, "BasicWindow",
+			"SourceSnapping", true);
 	config_set_default_bool(globalConfig, "BasicWindow",
 			"CenterSnapping", false);
 	config_set_default_double(globalConfig, "BasicWindow",

+ 86 - 2
obs/window-basic-preview.cpp

@@ -502,6 +502,66 @@ static bool AddItemBounds(obs_scene_t *scene, obs_sceneitem_t *item,
 	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());
@@ -519,11 +579,35 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset)
 
 	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;
 
-	offset.x += snapOffset.x;
-	offset.y += snapOffset.y;
+	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)

+ 9 - 0
obs/window-basic-settings.cpp

@@ -269,6 +269,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
 	HookWidget(ui->snappingEnabled,      CHECK_CHANGED,  GENERAL_CHANGED);
 	HookWidget(ui->screenSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);
 	HookWidget(ui->centerSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);
+	HookWidget(ui->sourceSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);
 	HookWidget(ui->snapDistance,         SCROLL_CHANGED, GENERAL_CHANGED);
 	HookWidget(ui->outputMode,           COMBO_CHANGED,  OUTPUTS_CHANGED);
 	HookWidget(ui->streamType,           COMBO_CHANGED,  STREAM1_CHANGED);
@@ -779,6 +780,10 @@ void OBSBasicSettings::LoadGeneralSettings()
 			"BasicWindow", "CenterSnapping");
 	ui->centerSnapping->setChecked(centerSnapping);
 
+	bool sourceSnapping = config_get_bool(GetGlobalConfig(),
+			"BasicWindow", "SourceSnapping");
+	ui->sourceSnapping->setChecked(sourceSnapping);
+
 	double snapDistance = config_get_double(GetGlobalConfig(),
 			"BasicWindow", "SnapDistance");
 	ui->snapDistance->setValue(snapDistance);
@@ -2071,6 +2076,10 @@ void OBSBasicSettings::SaveGeneralSettings()
 		config_set_bool(GetGlobalConfig(), "BasicWindow",
 				"CenterSnapping",
 				ui->centerSnapping->isChecked());
+	if (WidgetChanged(ui->sourceSnapping))
+		config_set_bool(GetGlobalConfig(), "BasicWindow",
+				"SourceSnapping",
+				ui->sourceSnapping->isChecked());
 	if (WidgetChanged(ui->snapDistance))
 		config_set_double(GetGlobalConfig(), "BasicWindow",
 				"SnapDistance",