From fce078d997a40209fdfe51428b0572c342b878ef Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 17 Jun 2017 19:10:42 -0500 Subject: [PATCH] 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 --- UI/CMakeLists.txt | 2 ++ UI/forms/images/locked_mask.png | Bin 0 -> 288 bytes UI/forms/images/unlocked_mask.png | Bin 0 -> 318 bytes UI/forms/obs.qrc | 2 ++ UI/locked-checkbox.cpp | 36 +++++++++++++++++++++++++++ UI/locked-checkbox.hpp | 17 +++++++++++++ UI/visibility-item-widget.cpp | 39 ++++++++++++++++++++++++++++++ UI/visibility-item-widget.hpp | 5 ++++ UI/window-basic-main.cpp | 3 +++ UI/window-basic-preview.cpp | 9 +++++++ libobs/obs-scene.c | 26 ++++++++++++++++++++ libobs/obs-scene.h | 1 + libobs/obs.h | 4 +++ 13 files changed, 144 insertions(+) create mode 100644 UI/forms/images/locked_mask.png create mode 100644 UI/forms/images/unlocked_mask.png create mode 100644 UI/locked-checkbox.cpp create mode 100644 UI/locked-checkbox.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 129351497..6e04bbd21 100644 --- a/UI/CMakeLists.txt +++ b/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 diff --git a/UI/forms/images/locked_mask.png b/UI/forms/images/locked_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..282cac22258b35675f923dbdaac18e841f718530 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPtn-HV5PPWOz`#_s&cNvf(yn*!Aa_AlV(i{IvwqB{`I!A*59V>U(a&w%Jp@xxG(-t{XRgIA~z-`ChZR z&r;@@mPZ)P0u#Glcg)zbGqk;e-8~{}t(Zl3NBgsa3qtE3ZRnU^*jQ{M_D;k2dNuEc zZqqXceI2(ub{v+-k2rX5y5O$~Gu3Y#^L2^&=DK3m-p6cK{Sifb#SVWriceU4P&SeK z`wk~rLH*OIl|2v5FTa^yb?D^*{g3${#Ch!gFF5w6{g#v+L-S-)PTK`(kAdD|@O1Ta JS?83{1OPr_d)NQ~ literal 0 HcmV?d00001 diff --git a/UI/forms/obs.qrc b/UI/forms/obs.qrc index 361fef4ae..b6b6e86fa 100644 --- a/UI/forms/obs.qrc +++ b/UI/forms/obs.qrc @@ -14,6 +14,8 @@ images/up.png images/obs.png images/tray_active.png + images/locked_mask.png + images/unlocked_mask.png images/settings/advanced.png diff --git a/UI/locked-checkbox.cpp b/UI/locked-checkbox.cpp new file mode 100644 index 000000000..91a049fac --- /dev/null +++ b/UI/locked-checkbox.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include "locked-checkbox.hpp" + +#include + +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)); +} diff --git a/UI/locked-checkbox.hpp b/UI/locked-checkbox.hpp new file mode 100644 index 000000000..017470f72 --- /dev/null +++ b/UI/locked-checkbox.hpp @@ -0,0 +1,17 @@ +#include +#include + +class QPaintEvernt; + +class LockedCheckBox : public QCheckBox { + Q_OBJECT + + QPixmap lockedImage; + QPixmap unlockedImage; + +public: + LockedCheckBox(); + +protected: + void paintEvent(QPaintEvent *event) override; +}; diff --git a/UI/visibility-item-widget.cpp b/UI/visibility-item-widget.cpp index f42d193df..c856a781b 100644 --- a/UI/visibility-item-widget.cpp +++ b/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 @@ -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(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()) diff --git a/UI/visibility-item-widget.hpp b/UI/visibility-item-widget.hpp index 439cfd373..80c46db5d 100644 --- a/UI/visibility-item-widget.hpp +++ b/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); diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index e328d4a12..6ee2d4a83 100644 --- a/UI/window-basic-main.cpp +++ b/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(param); struct vec2 dir; struct vec2 pos; diff --git a/UI/window-basic-preview.cpp b/UI/window-basic-preview.cpp index 6cba59bba..43213f970 100644 --- a/UI/window-basic-preview.cpp +++ b/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(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); diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 32940215b..dc6b4d858 100644 --- a/libobs/obs-scene.c +++ b/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) { diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index d46022123..530334857 100644 --- a/libobs/obs-scene.h +++ b/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; diff --git a/libobs/obs.h b/libobs/obs.h index f9a55404b..cd4d2efd1 100644 --- a/libobs/obs.h +++ b/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);