diff --git a/obs/CMakeLists.txt b/obs/CMakeLists.txt index c66667ddd..761841e19 100644 --- a/obs/CMakeLists.txt +++ b/obs/CMakeLists.txt @@ -109,6 +109,7 @@ set(obs_SOURCES adv-audio-control.cpp visibility-checkbox.cpp vertical-scroll-area.cpp + visibility-item-widget.cpp source-list-widget.cpp crash-report.cpp qt-wrappers.cpp) @@ -139,6 +140,7 @@ set(obs_HEADERS adv-audio-control.hpp visibility-checkbox.hpp vertical-scroll-area.hpp + visibility-item-widget.hpp source-list-widget.hpp qt-display.hpp crash-report.hpp diff --git a/obs/visibility-item-widget.cpp b/obs/visibility-item-widget.cpp new file mode 100644 index 000000000..9bb04582f --- /dev/null +++ b/obs/visibility-item-widget.cpp @@ -0,0 +1,258 @@ +#include "visibility-item-widget.hpp" +#include "visibility-checkbox.hpp" +#include "qt-wrappers.hpp" +#include "obs-app.hpp" +#include +#include +#include +#include +#include + +VisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_) + : source (source_), + enabledSignal (obs_source_get_signal_handler(source), "enable", + OBSSourceEnabled, this), + renamedSignal (obs_source_get_signal_handler(source), "rename", + OBSSourceRenamed, this) +{ + const char *name = obs_source_get_name(source); + bool enabled = obs_source_enabled(source); + + vis = new VisibilityCheckBox(); + vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + /* Fix for non-apple systems where the spacing would be too big */ +#ifndef __APPLE__ + vis->setMaximumSize(16, 16); +#endif + vis->setChecked(enabled); + + label = new QLabel(QT_UTF8(name)); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QHBoxLayout *itemLayout = new QHBoxLayout(); + itemLayout->addWidget(vis); + itemLayout->addWidget(label); + itemLayout->setContentsMargins(5, 2, 5, 2); + + setLayout(itemLayout); + setStyleSheet("background-color: rgba(255, 255, 255, 0);"); + + connect(vis, SIGNAL(clicked(bool)), + this, SLOT(VisibilityClicked(bool))); +} + +VisibilityItemWidget::VisibilityItemWidget(obs_sceneitem_t *item_) + : item (item_), + source (obs_sceneitem_get_source(item)), + renamedSignal (obs_source_get_signal_handler(source), "rename", + OBSSourceRenamed, this) +{ + const char *name = obs_source_get_name(source); + bool enabled = obs_sceneitem_visible(item); + obs_scene_t *scene = obs_sceneitem_get_scene(item); + obs_source_t *sceneSource = obs_scene_get_source(scene); + + vis = new VisibilityCheckBox(); + vis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + /* Fix for non-apple systems where the spacing would be too big */ +#ifndef __APPLE__ + vis->setMaximumSize(16, 16); +#endif + vis->setChecked(enabled); + + label = new QLabel(QT_UTF8(name)); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QHBoxLayout *itemLayout = new QHBoxLayout(); + itemLayout->addWidget(vis); + itemLayout->addWidget(label); + itemLayout->setContentsMargins(5, 2, 5, 2); + + setLayout(itemLayout); + setStyleSheet("background-color: rgba(255, 255, 255, 0);"); + + signal_handler_t *signal = obs_source_get_signal_handler(sceneSource); + signal_handler_connect(signal, "remove", OBSSceneRemove, this); + signal_handler_connect(signal, "item_remove", OBSSceneItemRemove, + this); + signal_handler_connect(signal, "item_visible", OBSSceneItemVisible, + this); + + connect(vis, SIGNAL(clicked(bool)), + this, SLOT(VisibilityClicked(bool))); +} + +VisibilityItemWidget::~VisibilityItemWidget() +{ + DisconnectItemSignals(); +} + +void VisibilityItemWidget::DisconnectItemSignals() +{ + if (!item || sceneRemoved) + return; + + obs_scene_t *scene = obs_sceneitem_get_scene(item); + obs_source_t *sceneSource = obs_scene_get_source(scene); + signal_handler_t *signal = obs_source_get_signal_handler(sceneSource); + + signal_handler_disconnect(signal, "remove", OBSSceneRemove, this); + signal_handler_disconnect(signal, "item_remove", OBSSceneItemRemove, + this); + signal_handler_disconnect(signal, "item_visible", OBSSceneItemVisible, + this); + + sceneRemoved = true; +} + +void VisibilityItemWidget::OBSSceneRemove(void *param, calldata_t *data) +{ + VisibilityItemWidget *window = + reinterpret_cast(param); + + window->DisconnectItemSignals(); + + UNUSED_PARAMETER(data); +} + +void VisibilityItemWidget::OBSSceneItemRemove(void *param, calldata_t *data) +{ + VisibilityItemWidget *window = + reinterpret_cast(param); + obs_sceneitem_t *item = (obs_sceneitem_t*)calldata_ptr(data, "item"); + + if (item == window->item) + window->DisconnectItemSignals(); +} + +void VisibilityItemWidget::OBSSceneItemVisible(void *param, calldata_t *data) +{ + VisibilityItemWidget *window = + reinterpret_cast(param); + obs_sceneitem_t *curItem = (obs_sceneitem_t*)calldata_ptr(data, "item"); + bool enabled = calldata_bool(data, "visible"); + + if (window->item == curItem) + QMetaObject::invokeMethod(window, "SourceEnabled", + Q_ARG(bool, enabled)); +} + +void VisibilityItemWidget::OBSSourceEnabled(void *param, calldata_t *data) +{ + VisibilityItemWidget *window = + reinterpret_cast(param); + bool enabled = calldata_bool(data, "enabled"); + + QMetaObject::invokeMethod(window, "SourceEnabled", + Q_ARG(bool, enabled)); +} + +void VisibilityItemWidget::OBSSourceRenamed(void *param, calldata_t *data) +{ + VisibilityItemWidget *window = + reinterpret_cast(param); + const char *name = calldata_string(data, "new_name"); + + QMetaObject::invokeMethod(window, "SourceRenamed", + Q_ARG(QString, QT_UTF8(name))); +} + +void VisibilityItemWidget::VisibilityClicked(bool visible) +{ + if (item) + obs_sceneitem_set_visible(item, visible); + else + obs_source_set_enabled(source, visible); +} + +void VisibilityItemWidget::SourceEnabled(bool enabled) +{ + if (vis->isChecked() != enabled) + vis->setChecked(enabled); +} + +void VisibilityItemWidget::SourceRenamed(QString name) +{ + if (label && name != label->text()) + label->setText(name); +} + +void VisibilityItemWidget::SetColor(const QColor &color, + bool active_, bool selected_) +{ + /* Do not update unless the state has actually changed */ + if (active_ == active && selected_ == selected) + return; + + QPalette pal = vis->palette(); + pal.setColor(QPalette::WindowText, color); + vis->setPalette(pal); + + label->setStyleSheet(QString("color: %1;").arg(color.name())); + + active = active_; + selected = selected_; +} + +VisibilityItemDelegate::VisibilityItemDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +void VisibilityItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyledItemDelegate::paint(painter, option, index); + + QObject *parentObj = parent(); + QListWidget *list = qobject_cast(parentObj); + if (!list) + return; + + QListWidgetItem *item = list->item(index.row()); + VisibilityItemWidget *widget = + qobject_cast(list->itemWidget(item)); + if (!widget) + return; + + bool selected = option.state.testFlag(QStyle::State_Selected); + bool active = option.state.testFlag(QStyle::State_Active); + + QPalette palette = list->palette(); + QPalette::ColorGroup group = active ? + QPalette::Active : QPalette::Inactive; + +#ifdef _WIN32 + QPalette::ColorRole highlightRole = QPalette::WindowText; +#else + QPalette::ColorRole highlightRole = QPalette::HighlightedText; +#endif + + QPalette::ColorRole role; + + if (selected && active) + role = highlightRole; + else + role = QPalette::WindowText; + + widget->SetColor(palette.color(group, role), active, selected); +} + +void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, + obs_source_t *source) +{ + VisibilityItemWidget *baseWidget = new VisibilityItemWidget(source); + + item->setSizeHint(baseWidget->sizeHint()); + list->setItemWidget(item, baseWidget); +} + +void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, + obs_sceneitem_t *sceneItem) +{ + VisibilityItemWidget *baseWidget = new VisibilityItemWidget(sceneItem); + + item->setSizeHint(baseWidget->sizeHint()); + list->setItemWidget(item, baseWidget); +} diff --git a/obs/visibility-item-widget.hpp b/obs/visibility-item-widget.hpp new file mode 100644 index 000000000..439cfd373 --- /dev/null +++ b/obs/visibility-item-widget.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +class QLabel; +class QLineEdit; +class QListWidget; +class QListWidgetItem; +class VisibilityCheckBox; + +class VisibilityItemWidget : public QWidget { + Q_OBJECT + +private: + OBSSceneItem item; + OBSSource source; + QLabel *label = nullptr; + VisibilityCheckBox *vis = nullptr; + QString oldName; + + OBSSignal sceneRemoveSignal; + OBSSignal enabledSignal; + OBSSignal renamedSignal; + bool sceneRemoved = false; + + bool active = false; + bool selected = false; + + 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 OBSSourceEnabled(void *param, calldata_t *data); + static void OBSSourceRenamed(void *param, calldata_t *data); + + void DisconnectItemSignals(); + +private slots: + void VisibilityClicked(bool visible); + void SourceEnabled(bool enabled); + void SourceRenamed(QString name); + +public: + VisibilityItemWidget(obs_source_t *source); + VisibilityItemWidget(obs_sceneitem_t *item); + ~VisibilityItemWidget(); + + void SetColor(const QColor &color, bool active, bool selected); +}; + +class VisibilityItemDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + VisibilityItemDelegate(QObject *parent = nullptr); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; +}; + +void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, + obs_source_t *source); +void SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, + obs_sceneitem_t *sceneItem);