Browse Source

UI, libobs: Add ability to lock individual scene items

Adds a lock checkbox to allow the user to lock a specific scene item.

Closes jp9000/obs-studio#949
cg2121 8 years ago
parent
commit
fce078d997

+ 2 - 0
UI/CMakeLists.txt

@@ -161,6 +161,7 @@ set(obs_SOURCES
 	adv-audio-control.cpp
 	item-widget-helpers.cpp
 	visibility-checkbox.cpp
+	locked-checkbox.cpp
 	vertical-scroll-area.cpp
 	visibility-item-widget.cpp
 	slider-absoluteset-style.cpp
@@ -208,6 +209,7 @@ set(obs_HEADERS
 	adv-audio-control.hpp
 	item-widget-helpers.hpp
 	visibility-checkbox.hpp
+	locked-checkbox.hpp
 	vertical-scroll-area.hpp
 	visibility-item-widget.hpp
 	slider-absoluteset-style.hpp

BIN
UI/forms/images/locked_mask.png


BIN
UI/forms/images/unlocked_mask.png


+ 2 - 0
UI/forms/obs.qrc

@@ -14,6 +14,8 @@
     <file>images/up.png</file>
     <file>images/obs.png</file>
     <file>images/tray_active.png</file>
+    <file>images/locked_mask.png</file>
+    <file>images/unlocked_mask.png</file>
   </qresource>
   <qresource prefix="/settings">
     <file>images/settings/advanced.png</file>

+ 36 - 0
UI/locked-checkbox.cpp

@@ -0,0 +1,36 @@
+#include <QPaintEvent>
+#include <QPixmap>
+#include <QPainter>
+#include "locked-checkbox.hpp"
+
+#include <util/c99defs.h>
+
+LockedCheckBox::LockedCheckBox() : QCheckBox()
+{
+	lockedImage =
+		QPixmap::fromImage(QImage(":/res/images/locked_mask.png"));
+	unlockedImage =
+		QPixmap::fromImage(QImage(":/res/images/unlocked_mask.png"));
+	setMinimumSize(16, 16);
+
+	setStyleSheet("outline: none;");
+}
+
+void LockedCheckBox::paintEvent(QPaintEvent *event)
+{
+	UNUSED_PARAMETER(event);
+
+	QPixmap &pixmap = isChecked() ? lockedImage : unlockedImage;
+	QImage image(pixmap.size(), QImage::Format_ARGB32);
+
+	QPainter draw(&image);
+	draw.setCompositionMode(QPainter::CompositionMode_Source);
+	draw.drawPixmap(0, 0, pixmap.width(), pixmap.height(), pixmap);
+	draw.setCompositionMode(QPainter::CompositionMode_SourceIn);
+	draw.fillRect(QRectF(QPointF(0.0f, 0.0f), pixmap.size()),
+			palette().color(foregroundRole()));
+
+	QPainter p(this);
+	p.drawPixmap(0, 0, image.width(), image.height(),
+			QPixmap::fromImage(image));
+}

+ 17 - 0
UI/locked-checkbox.hpp

@@ -0,0 +1,17 @@
+#include <QCheckBox>
+#include <QPixmap>
+
+class QPaintEvernt;
+
+class LockedCheckBox : public QCheckBox {
+	Q_OBJECT
+
+	QPixmap lockedImage;
+	QPixmap unlockedImage;
+
+public:
+	LockedCheckBox();
+
+protected:
+	void paintEvent(QPaintEvent *event) override;
+};

+ 39 - 0
UI/visibility-item-widget.cpp

@@ -1,5 +1,6 @@
 #include "visibility-item-widget.hpp"
 #include "visibility-checkbox.hpp"
+#include "locked-checkbox.hpp"
 #include "qt-wrappers.hpp"
 #include "obs-app.hpp"
 #include <QListWidget>
@@ -49,6 +50,7 @@ VisibilityItemWidget::VisibilityItemWidget(obs_sceneitem_t *item_)
 {
 	const char *name = obs_source_get_name(source);
 	bool enabled = obs_sceneitem_visible(item);
+	bool locked = obs_sceneitem_locked(item);
 	obs_scene_t *scene = obs_sceneitem_get_scene(item);
 	obs_source_t *sceneSource = obs_scene_get_source(scene);
 
@@ -60,13 +62,23 @@ VisibilityItemWidget::VisibilityItemWidget(obs_sceneitem_t *item_)
 #endif
 	vis->setChecked(enabled);
 
+	lock = new LockedCheckBox();
+	lock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+	/* Fix for non-apple systems where the spacing would be too big */
+#ifndef __APPLE__
+	lock->setMaximumSize(16, 16);
+#endif
+	lock->setChecked(locked);
+
 	label = new QLabel(QT_UTF8(name));
 	label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
 
 	QHBoxLayout *itemLayout = new QHBoxLayout();
 	itemLayout->addWidget(vis);
+	itemLayout->addWidget(lock);
 	itemLayout->addWidget(label);
 	itemLayout->setContentsMargins(5, 2, 5, 2);
+	itemLayout->setSpacing(2);
 
 	setLayout(itemLayout);
 	setStyleSheet("background-color: rgba(255, 255, 255, 0);");
@@ -80,6 +92,9 @@ VisibilityItemWidget::VisibilityItemWidget(obs_sceneitem_t *item_)
 
 	connect(vis, SIGNAL(clicked(bool)),
 			this, SLOT(VisibilityClicked(bool)));
+
+	connect(lock, SIGNAL(clicked(bool)),
+			this, SLOT(LockClicked(bool)));
 }
 
 VisibilityItemWidget::~VisibilityItemWidget()
@@ -137,6 +152,18 @@ void VisibilityItemWidget::OBSSceneItemVisible(void *param, calldata_t *data)
 				Q_ARG(bool, enabled));
 }
 
+void VisibilityItemWidget::OBSSceneItemLocked(void *param, calldata_t *data)
+{
+	VisibilityItemWidget *window =
+		reinterpret_cast<VisibilityItemWidget*>(param);
+	obs_sceneitem_t *curItem = (obs_sceneitem_t*)calldata_ptr(data, "item");
+	bool locked = calldata_bool(data, "locked");
+
+	if (window->item == curItem)
+		QMetaObject::invokeMethod(window, "SourceLocked",
+				Q_ARG(bool, locked));
+}
+
 void VisibilityItemWidget::OBSSourceEnabled(void *param, calldata_t *data)
 {
 	VisibilityItemWidget *window =
@@ -165,12 +192,24 @@ void VisibilityItemWidget::VisibilityClicked(bool visible)
 		obs_source_set_enabled(source, visible);
 }
 
+void VisibilityItemWidget::LockClicked(bool locked)
+{
+	if (item)
+		obs_sceneitem_set_locked(item, locked);
+}
+
 void VisibilityItemWidget::SourceEnabled(bool enabled)
 {
 	if (vis->isChecked() != enabled)
 		vis->setChecked(enabled);
 }
 
+void VisibilityItemWidget::SourceLocked(bool locked)
+{
+	if (lock->isChecked() != locked)
+		lock->setChecked(locked);
+}
+
 void VisibilityItemWidget::SourceRenamed(QString name)
 {
 	if (label && name != label->text())

+ 5 - 0
UI/visibility-item-widget.hpp

@@ -9,6 +9,7 @@ class QLineEdit;
 class QListWidget;
 class QListWidgetItem;
 class VisibilityCheckBox;
+class LockedCheckBox;
 
 class VisibilityItemWidget : public QWidget {
 	Q_OBJECT
@@ -18,6 +19,7 @@ private:
 	OBSSource source;
 	QLabel *label = nullptr;
 	VisibilityCheckBox *vis = nullptr;
+	LockedCheckBox *lock = nullptr;
 	QString oldName;
 
 	OBSSignal sceneRemoveSignal;
@@ -31,6 +33,7 @@ private:
 	static void OBSSceneRemove(void *param, calldata_t *data);
 	static void OBSSceneItemRemove(void *param, calldata_t *data);
 	static void OBSSceneItemVisible(void *param, calldata_t *data);
+	static void OBSSceneItemLocked(void *param, calldata_t *data);
 	static void OBSSourceEnabled(void *param, calldata_t *data);
 	static void OBSSourceRenamed(void *param, calldata_t *data);
 
@@ -38,8 +41,10 @@ private:
 
 private slots:
 	void VisibilityClicked(bool visible);
+	void LockClicked(bool locked);
 	void SourceEnabled(bool enabled);
 	void SourceRenamed(QString name);
+	void SourceLocked(bool locked);
 
 public:
 	VisibilityItemWidget(obs_source_t *source);

+ 3 - 0
UI/window-basic-main.cpp

@@ -5004,6 +5004,9 @@ void OBSBasic::Nudge(int dist, MoveDir dir)
 
 	auto func = [] (obs_scene_t*, obs_sceneitem_t *item, void *param)
 	{
+		if (obs_sceneitem_locked(item))
+			return true;
+
 		MoveInfo *info = reinterpret_cast<MoveInfo*>(param);
 		struct vec2 dir;
 		struct vec2 pos;

+ 9 - 0
UI/window-basic-preview.cpp

@@ -75,6 +75,8 @@ static bool FindItemAtPos(obs_scene_t *scene, obs_sceneitem_t *item,
 
 	if (!SceneItemHasVideo(item))
 		return true;
+	if (obs_sceneitem_locked(item))
+		return true;
 
 	vec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);
 
@@ -674,6 +676,9 @@ void OBSBasicPreview::SnapItemMovement(vec2 &offset)
 
 static bool move_items(obs_scene_t *scene, obs_sceneitem_t *item, void *param)
 {
+	if (obs_sceneitem_locked(item))
+		return true;
+
 	vec2 *offset = reinterpret_cast<vec2*>(param);
 
 	if (obs_sceneitem_selected(item)) {
@@ -1084,6 +1089,9 @@ static inline bool crop_enabled(const obs_sceneitem_crop *crop)
 bool OBSBasicPreview::DrawSelectedItem(obs_scene_t *scene,
 		obs_sceneitem_t *item, void *param)
 {
+	if (obs_sceneitem_locked(item))
+		return true;
+
 	if (!obs_sceneitem_selected(item))
 		return true;
 
@@ -1183,6 +1191,7 @@ void OBSBasicPreview::DrawSceneEditing()
 	gs_technique_begin_pass(tech, 0);
 
 	OBSScene scene = main->GetCurrentScene();
+
 	if (scene)
 		obs_scene_enum_items(scene, DrawSelectedItem, this);
 

+ 26 - 0
libobs/obs-scene.c

@@ -590,6 +590,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 	const char            *scale_filter_str;
 	struct obs_scene_item *item;
 	bool visible;
+	bool lock;
 
 	if (!source) {
 		blog(LOG_WARNING, "[scene_load_item] Source %s not found!",
@@ -616,10 +617,12 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 	item->rot     = (float)obs_data_get_double(item_data, "rot");
 	item->align   = (uint32_t)obs_data_get_int(item_data, "align");
 	visible = obs_data_get_bool(item_data, "visible");
+	lock = obs_data_get_bool(item_data, "locked");
 	obs_data_get_vec2(item_data, "pos",    &item->pos);
 	obs_data_get_vec2(item_data, "scale",  &item->scale);
 
 	set_visibility(item, visible);
+	obs_sceneitem_set_locked(item, lock);
 
 	item->bounds_type =
 		(enum obs_bounds_type)obs_data_get_int(item_data,
@@ -697,6 +700,7 @@ static void scene_save_item(obs_data_array_t *array,
 
 	obs_data_set_string(item_data, "name",         name);
 	obs_data_set_bool  (item_data, "visible",      item->user_visible);
+	obs_data_set_bool  (item_data, "locked",       item->locked);
 	obs_data_set_double(item_data, "rot",          item->rot);
 	obs_data_set_vec2 (item_data, "pos",          &item->pos);
 	obs_data_set_vec2 (item_data, "scale",        &item->scale);
@@ -1347,6 +1351,7 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
 	item->align   = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
 	item->actions_mutex = mutex;
 	item->user_visible = true;
+	item->locked = false;
 	os_atomic_set_long(&item->active_refs, 1);
 	vec2_set(&item->scale, 1.0f, 1.0f);
 	matrix4_identity(&item->draw_transform);
@@ -1773,6 +1778,27 @@ bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)
 	return true;
 }
 
+bool obs_sceneitem_locked(const obs_sceneitem_t *item)
+{
+	return item ? item->locked : false;
+}
+
+bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock)
+{
+	if (!item)
+		return false;
+
+	if (item->locked == lock)
+		return false;
+
+	if (!item->parent)
+		return false;
+
+	item->locked = lock;
+
+	return true;
+}
+
 static bool sceneitems_match(obs_scene_t *scene, obs_sceneitem_t * const *items,
 		size_t size, bool *order_matches)
 {

+ 1 - 0
libobs/obs-scene.h

@@ -41,6 +41,7 @@ struct obs_scene_item {
 	bool                  user_visible;
 	bool                  visible;
 	bool                  selected;
+	bool                  locked;
 
 	gs_texrender_t        *item_render;
 	struct obs_sceneitem_crop crop;

+ 4 - 0
libobs/obs.h

@@ -1251,8 +1251,12 @@ EXPORT obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item);
 /** Gets the source of a scene item. */
 EXPORT obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item);
 
+/* FIXME: The following functions should be deprecated and replaced with a way
+ * to specify savable private user data. -Jim */
 EXPORT void obs_sceneitem_select(obs_sceneitem_t *item, bool select);
 EXPORT bool obs_sceneitem_selected(const obs_sceneitem_t *item);
+EXPORT bool obs_sceneitem_locked(const obs_sceneitem_t *item);
+EXPORT bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock);
 
 /* Functions for getting/setting specific orientation of a scene item */
 EXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos);