Restructure "src/" folder

Moving files from the "src/" folder into "src/legacy", "src/macro-core",
and "src/utils" was necessary as it was becoming a bit too cluttered.
This commit is contained in:
WarmUpTill
2022-08-14 04:10:30 +02:00
committed by WarmUpTill
parent 5ae9e18044
commit 53a5fa6ff4
224 changed files with 589 additions and 895 deletions

79
src/utils/curl-helper.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include <QDir>
#include <QFileInfo>
#include <curl/curl.h>
#include <obs.hpp>
#include "curl-helper.hpp"
initFunction f_curl_init = nullptr;
setOptFunction f_curl_setopt = nullptr;
performFunction f_curl_perform = nullptr;
cleanupFunction f_curl_cleanup = nullptr;
QLibrary *loaded_curl_lib = nullptr;
bool resolveCurl()
{
f_curl_init = (initFunction)loaded_curl_lib->resolve("curl_easy_init");
f_curl_setopt =
(setOptFunction)loaded_curl_lib->resolve("curl_easy_setopt");
f_curl_perform =
(performFunction)loaded_curl_lib->resolve("curl_easy_perform");
f_curl_cleanup =
(cleanupFunction)loaded_curl_lib->resolve("curl_easy_cleanup");
if (f_curl_init && f_curl_setopt && f_curl_perform && f_curl_cleanup) {
blog(LOG_INFO, "[adv-ss] curl loaded successfully");
return true;
}
blog(LOG_INFO, "[adv-ss] curl symbols not resolved");
return false;
}
bool loadCurl()
{
loaded_curl_lib = new QLibrary(curl_library_name, nullptr);
if (resolveCurl()) {
blog(LOG_INFO, "[adv-ss] found curl library");
return true;
} else {
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
blog(LOG_WARNING,
"[adv-ss] couldn't find the curl library in PATH");
}
QStringList locations;
locations << QDir::currentPath();
#if defined(__linux__) || defined(__APPLE__)
locations << "/usr/lib";
locations << "/usr/local/lib";
locations << "/usr/lib/x86_64-linux-gnu";
locations << "/usr/local/opt/curl/lib";
#endif
for (QString path : locations) {
blog(LOG_INFO, "[adv-ss] trying '%s'",
path.toUtf8().constData());
QFileInfo libPath(
QDir(path).absoluteFilePath(curl_library_name));
if (libPath.exists() && libPath.isFile()) {
QString libFilePath = libPath.absoluteFilePath();
blog(LOG_INFO, "[adv-ss] found curl library at '%s'",
libFilePath.toUtf8().constData());
loaded_curl_lib = new QLibrary(libFilePath, nullptr);
if (resolveCurl()) {
return true;
} else {
delete loaded_curl_lib;
loaded_curl_lib = nullptr;
}
}
}
blog(LOG_WARNING, "[adv-ss] can't find the curl library");
return false;
}

26
src/utils/curl-helper.hpp Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <curl/curl.h>
#include <QLibrary>
#if defined(WIN32)
constexpr auto curl_library_name = "libcurl.dll";
#elif __APPLE__
constexpr auto curl_library_name = "libcurl.4.dylib";
#else
constexpr auto curl_library_name = "libcurl.so.4";
#endif
typedef CURL *(*initFunction)(void);
typedef CURLcode (*setOptFunction)(CURL *, CURLoption, ...);
typedef CURLcode (*performFunction)(CURL *);
typedef void (*cleanupFunction)(CURL *);
extern initFunction f_curl_init;
extern setOptFunction f_curl_setopt;
extern performFunction f_curl_perform;
extern cleanupFunction f_curl_cleanup;
extern QLibrary *loaded_curl_lib;
bool resolveCurl();
bool loadCurl();

View File

@@ -0,0 +1,168 @@
#include "duration-control.hpp"
#include "utility.hpp"
#include "obs-module.h"
#include <sstream>
#include <iomanip>
#include <QHBoxLayout>
void Duration::Save(obs_data_t *obj, const char *secondsName,
const char *unitName)
{
obs_data_set_double(obj, secondsName, seconds);
obs_data_set_int(obj, unitName, static_cast<int>(displayUnit));
}
void Duration::Load(obs_data_t *obj, const char *secondsName,
const char *unitName)
{
seconds = obs_data_get_double(obj, secondsName);
displayUnit =
static_cast<DurationUnit>(obs_data_get_int(obj, unitName));
}
bool Duration::DurationReached()
{
if (IsReset()) {
_startTime = std::chrono::high_resolution_clock::now();
}
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - _startTime);
return runTime.count() >= seconds * 1000;
}
bool Duration::IsReset()
{
return _startTime.time_since_epoch().count() == 0;
}
double Duration::TimeRemaining()
{
if (IsReset()) {
return seconds;
}
auto runTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - _startTime);
if (runTime.count() >= seconds * 1000) {
return 0;
}
return (seconds * 1000 - runTime.count()) / 1000.;
}
void Duration::SetTimeRemaining(double remaining)
{
long long msPassed = (seconds - remaining) * 1000;
_startTime = std::chrono::high_resolution_clock::now() -
std::chrono::milliseconds(msPassed);
}
void Duration::Reset()
{
_startTime = {};
}
int durationUnitToMultiplier(DurationUnit u)
{
switch (u) {
case DurationUnit::SECONDS:
return 1;
case DurationUnit::MINUTES:
return 60;
case DurationUnit::HOURS:
return 3600;
default:
break;
}
return 0;
}
std::string durationUnitToString(DurationUnit u)
{
switch (u) {
case DurationUnit::SECONDS:
return obs_module_text("AdvSceneSwitcher.unit.secends");
case DurationUnit::MINUTES:
return obs_module_text("AdvSceneSwitcher.unit.minutes");
case DurationUnit::HOURS:
return obs_module_text("AdvSceneSwitcher.unit.hours");
default:
break;
}
return "";
}
std::string Duration::ToString()
{
std::ostringstream ss;
ss << std::fixed << std::setprecision(2)
<< seconds / durationUnitToMultiplier(displayUnit) << " "
<< durationUnitToString(displayUnit);
return ss.str();
}
static void populateUnits(QComboBox *list)
{
list->addItem(obs_module_text("AdvSceneSwitcher.unit.secends"));
list->addItem(obs_module_text("AdvSceneSwitcher.unit.minutes"));
list->addItem(obs_module_text("AdvSceneSwitcher.unit.hours"));
}
DurationSelection::DurationSelection(QWidget *parent, bool showUnitSelection)
: QWidget(parent), _unitMultiplier(1)
{
_duration = new QDoubleSpinBox(parent);
_duration->setMaximum(86400); // 24 hours
_unitSelection = new QComboBox();
populateUnits(_unitSelection);
QWidget::connect(_duration, SIGNAL(valueChanged(double)), this,
SLOT(_DurationChanged(double)));
QWidget::connect(_unitSelection, SIGNAL(currentIndexChanged(int)), this,
SLOT(_UnitChanged(int)));
QHBoxLayout *layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(11);
layout->addWidget(_duration);
if (showUnitSelection) {
layout->addWidget(_unitSelection);
}
setLayout(layout);
}
void DurationSelection::SetValue(double value)
{
_duration->setValue(value / _unitMultiplier);
}
void DurationSelection::SetUnit(DurationUnit u)
{
_unitSelection->setCurrentIndex(static_cast<int>(u));
}
void DurationSelection::SetDuration(Duration d)
{
SetUnit(d.displayUnit);
SetValue(d.seconds);
}
void DurationSelection::_DurationChanged(double value)
{
emit DurationChanged(value * _unitMultiplier);
}
void DurationSelection::_UnitChanged(int idx)
{
DurationUnit unit = static_cast<DurationUnit>(idx);
double prevMultiplier = _unitMultiplier;
_unitMultiplier = durationUnitToMultiplier(unit);
_duration->setValue(_duration->value() *
(prevMultiplier / _unitMultiplier));
emit UnitChanged(unit);
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include <QWidget>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QPushButton>
#include <chrono>
#include "obs-data.h"
enum class DurationUnit {
SECONDS,
MINUTES,
HOURS,
};
class Duration {
public:
void Save(obs_data_t *obj, const char *secondsName = "seconds",
const char *unitName = "displayUnit");
void Load(obs_data_t *obj, const char *secondsName = "seconds",
const char *unitName = "displayUnit");
bool DurationReached();
bool IsReset();
double TimeRemaining();
void SetTimeRemaining(double);
void Reset();
std::string ToString();
double seconds = 0.;
// only used for UI
DurationUnit displayUnit = DurationUnit::SECONDS;
private:
std::chrono::high_resolution_clock::time_point _startTime;
};
class DurationSelection : public QWidget {
Q_OBJECT
public:
DurationSelection(QWidget *parent = nullptr,
bool showUnitSelection = true);
void SetValue(double value);
void SetUnit(DurationUnit u);
void SetDuration(Duration d);
QDoubleSpinBox *SpinBox() { return _duration; }
private slots:
void _DurationChanged(double value);
void _UnitChanged(int idx);
signals:
void DurationChanged(double value); // always reutrn value in seconds
void UnitChanged(DurationUnit u);
private:
QDoubleSpinBox *_duration;
QComboBox *_unitSelection;
double _unitMultiplier;
};

View File

@@ -0,0 +1,49 @@
#include "file-selection.hpp"
#include <obs-module.h>
#include <QLayout>
#include <QFileDialog>
FileSelection::FileSelection(FileSelection::Type type, QWidget *parent)
: QWidget(parent), _type(type)
{
_filePath = new QLineEdit();
_browseButton =
new QPushButton(obs_module_text("AdvSceneSwitcher.browse"));
QWidget::connect(_filePath, SIGNAL(editingFinished()), this,
SLOT(PathChange()));
QWidget::connect(_browseButton, SIGNAL(clicked()), this,
SLOT(BrowseButtonClicked()));
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(_filePath);
layout->addWidget(_browseButton);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
}
void FileSelection::SetPath(const QString &path)
{
_filePath->setText(path);
}
void FileSelection::BrowseButtonClicked()
{
QString path;
if (_type == FileSelection::Type::WRITE) {
path = QFileDialog::getSaveFileName(this);
} else {
path = QFileDialog::getOpenFileName(this);
}
if (path.isEmpty()) {
return;
}
_filePath->setText(path);
emit PathChanged(path);
}
void FileSelection::PathChange()
{
emit PathChanged(_filePath->text());
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <QLineEdit>
#include <QPushButton>
#include <QWidget>
class FileSelection : public QWidget {
Q_OBJECT
public:
enum class Type {
READ,
WRITE,
};
FileSelection(FileSelection::Type type = FileSelection::Type::READ,
QWidget *parent = 0);
void SetPath(const QString &);
QPushButton *Button() { return _browseButton; }
private slots:
void BrowseButtonClicked();
void PathChange();
signals:
void PathChanged(const QString &);
private:
Type _type;
QLineEdit *_filePath;
QPushButton *_browseButton;
};

69
src/utils/name-dialog.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include <QVBoxLayout>
#include <QDialogButtonBox>
#include "name-dialog.hpp"
AdvSSNameDialog::AdvSSNameDialog(QWidget *parent) : QDialog(parent)
{
setModal(true);
setWindowModality(Qt::WindowModality::WindowModal);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
setFixedWidth(555);
setMinimumHeight(100);
QVBoxLayout *layout = new QVBoxLayout;
setLayout(layout);
label = new QLabel(this);
layout->addWidget(label);
label->setText("Set Text");
userText = new QLineEdit(this);
layout->addWidget(userText);
QDialogButtonBox *buttonbox = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
layout->addWidget(buttonbox);
buttonbox->setCenterButtons(true);
connect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
static bool IsWhitespace(char ch)
{
return ch == ' ' || ch == '\t';
}
static void CleanWhitespace(std::string &str)
{
while (str.size() && IsWhitespace(str.back()))
str.erase(str.end() - 1);
while (str.size() && IsWhitespace(str.front()))
str.erase(str.begin());
}
bool AdvSSNameDialog::AskForName(QWidget *parent, const QString &title,
const QString &text,
std::string &userTextInput,
const QString &placeHolder, int maxSize,
bool clean)
{
if (maxSize <= 0 || maxSize > 32767) {
maxSize = 170;
}
AdvSSNameDialog dialog(parent);
dialog.setWindowTitle(title);
dialog.label->setText(text);
dialog.userText->setMaxLength(maxSize);
dialog.userText->setText(placeHolder);
dialog.userText->selectAll();
if (dialog.exec() != DialogCode::Accepted) {
return false;
}
userTextInput = dialog.userText->text().toUtf8().constData();
if (clean) {
CleanWhitespace(userTextInput);
}
return true;
}

24
src/utils/name-dialog.hpp Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
// Based on OBS's NameDialog
class AdvSSNameDialog : public QDialog {
Q_OBJECT
public:
AdvSSNameDialog(QWidget *parent);
// Returns true if user clicks OK, false otherwise
// userTextInput returns string that user typed into dialog
static bool AskForName(QWidget *parent, const QString &title,
const QString &text, std::string &userTextInput,
const QString &placeHolder = QString(""),
int maxSize = 170, bool clean = true);
private:
QLabel *label;
QLineEdit *userText;
};

View File

@@ -0,0 +1,31 @@
#include "resizing-text-edit.hpp"
ResizingPlainTextEdit::ResizingPlainTextEdit(QWidget *parent,
const int scrollAt,
const int minLines,
const int paddingLines)
: QPlainTextEdit(parent),
_scrollAt(scrollAt),
_minLines(minLines),
_paddingLines(paddingLines)
{
QWidget::connect(this, SIGNAL(textChanged()), this,
SLOT(ResizeTexteditArea()));
}
void ResizingPlainTextEdit::ResizeTexteditArea()
{
QFontMetrics f(font());
int rowHeight = f.lineSpacing();
int numLines = document()->blockCount();
if (numLines + _paddingLines < _minLines) {
setFixedHeight(_minLines * rowHeight);
} else if (numLines + _paddingLines < _scrollAt) {
setFixedHeight((numLines + _paddingLines) * rowHeight);
} else {
setFixedHeight(_scrollAt * rowHeight);
}
adjustSize();
updateGeometry();
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <QPlainTextEdit>
class ResizingPlainTextEdit : public QPlainTextEdit {
Q_OBJECT
public:
ResizingPlainTextEdit(QWidget *parent, const int scrollAt = 10,
const int minLines = 3,
const int paddingLines = 2);
private slots:
void ResizeTexteditArea();
private:
const int _scrollAt;
const int _minLines;
const int _paddingLines;
};

View File

@@ -0,0 +1,241 @@
#include "scene-item-selection.hpp"
#include <obs-module.h>
void SceneItemSelection::Save(obs_data_t *obj, const char *name,
const char *targetName, const char *idxName)
{
obs_data_set_int(obj, targetName, static_cast<int>(_target));
if (_target == SceneItemSelection::Target::INDIVIDUAL) {
obs_data_set_int(obj, idxName, _idx);
} else {
obs_data_set_int(obj, idxName, 0);
}
obs_data_set_string(obj, name, GetWeakSourceName(_sceneItem).c_str());
}
void SceneItemSelection::Load(obs_data_t *obj, const char *name,
const char *targetName, const char *idxName)
{
_target = static_cast<SceneItemSelection::Target>(
obs_data_get_int(obj, targetName));
_idx = obs_data_get_int(obj, idxName);
auto sceneItemName = obs_data_get_string(obj, name);
_sceneItem = GetWeakSourceByName(sceneItemName);
}
struct ItemCountData {
std::string name;
int count = 0;
};
static bool countSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)
{
auto data = reinterpret_cast<ItemCountData *>(ptr);
if (obs_sceneitem_is_group(item)) {
obs_scene_t *scene = obs_sceneitem_group_get_scene(item);
obs_scene_enum_items(scene, countSceneItem, ptr);
}
auto name = obs_source_get_name(obs_sceneitem_get_source(item));
if (name == data->name) {
data->count++;
}
return true;
}
int getCountOfSceneItemOccurance(SceneSelection &s, std::string &name,
bool enumAllScenes = true)
{
ItemCountData data{name};
if (enumAllScenes && (s.GetType() == SceneSelectionType::CURRENT ||
s.GetType() == SceneSelectionType::PREVIOUS)) {
auto enumScenes = [](void *param, obs_source_t *source) {
if (!source) {
return true;
}
auto data = reinterpret_cast<ItemCountData *>(param);
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, countSceneItem, data);
return true;
};
obs_enum_scenes(enumScenes, &data);
} else {
auto source = obs_weak_source_get_source(s.GetScene(false));
auto scene = obs_scene_from_source(source);
obs_scene_enum_items(scene, countSceneItem, &data);
obs_source_release(source);
}
return data.count;
}
std::vector<obs_scene_item *>
SceneItemSelection::GetSceneItems(SceneSelection &sceneSelection)
{
auto s = obs_weak_source_get_source(sceneSelection.GetScene(false));
auto scene = obs_scene_from_source(s);
auto name = GetWeakSourceName(_sceneItem);
int count = getCountOfSceneItemOccurance(sceneSelection, name, false);
auto items = getSceneItemsWithName(scene, name);
obs_source_release(s);
std::vector<obs_scene_item *> ret;
if (_target == SceneItemSelection::Target::ALL ||
_target == SceneItemSelection::Target::ANY) {
ret = items;
} else {
// Index order starts at the bottom and increases to the top
// As this might be confusing reverse that order internally
int idx = count - 1 - _idx;
if (idx >= 0 && idx < (int)items.size()) {
obs_sceneitem_addref(items[idx]);
ret.emplace_back(items[idx]);
}
for (auto item : items) {
obs_sceneitem_release(item);
}
}
return ret;
}
std::string SceneItemSelection::ToString()
{
return GetWeakSourceName(_sceneItem);
}
SceneItemSelectionWidget::SceneItemSelectionWidget(QWidget *parent,
bool showAll,
AllSelectionType type)
: QWidget(parent), _showAll(showAll), _allType(type)
{
_sceneItems = new QComboBox();
_idx = new QComboBox();
_sceneItems->setSizeAdjustPolicy(QComboBox::AdjustToContents);
_idx->setSizeAdjustPolicy(QComboBox::AdjustToContents);
populateSceneItemSelection(_sceneItems);
QWidget::connect(_sceneItems,
SIGNAL(currentTextChanged(const QString &)), this,
SLOT(SelectionChanged(const QString &)));
QWidget::connect(_idx, SIGNAL(currentIndexChanged(int)), this,
SLOT(IdxChanged(int)));
auto layout = new QHBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_idx);
layout->addWidget(_sceneItems);
setLayout(layout);
_idx->hide();
}
void SceneItemSelectionWidget::SetSceneItem(const SceneItemSelection &item)
{
_sceneItems->setCurrentText(
QString::fromStdString(GetWeakSourceName(item._sceneItem)));
if (item._target == SceneItemSelection::Target::ALL) {
_allType = AllSelectionType::ALL;
_idx->setCurrentIndex(0);
} else if (item._target == SceneItemSelection::Target::ANY) {
_allType = AllSelectionType::ANY;
_idx->setCurrentIndex(0);
} else {
int idx = item._idx;
if (_showAll) {
idx += 1;
}
_idx->setCurrentIndex(idx);
}
}
void SceneItemSelectionWidget::SetScene(const SceneSelection &s)
{
_scene = s;
_sceneItems->clear();
_idx->hide();
populateSceneItemSelection(_sceneItems, _scene);
}
void SceneItemSelectionWidget::SetShowAll(bool value)
{
_showAll = value;
}
void SceneItemSelectionWidget::SetShowAllSelectionType(AllSelectionType t)
{
_allType = t;
_sceneItems->setCurrentIndex(0);
}
void SceneItemSelectionWidget::SceneChanged(const SceneSelection &s)
{
SetScene(s);
adjustSize();
}
void SceneItemSelectionWidget::SelectionChanged(const QString &name)
{
SceneItemSelection s;
_sceneItem = GetWeakSourceByQString(name);
s._sceneItem = _sceneItem;
if (_allType == AllSelectionType::ALL) {
s._target = SceneItemSelection::Target::ALL;
} else {
s._target = SceneItemSelection::Target::ANY;
}
auto stdName = name.toStdString();
int sceneItemCount = getCountOfSceneItemOccurance(_scene, stdName);
if (sceneItemCount > 1) {
_idx->show();
SetupIdxSelection(sceneItemCount);
} else {
_idx->hide();
}
emit SceneItemChanged(s);
}
void SceneItemSelectionWidget::IdxChanged(int idx)
{
if (idx < 0) {
return;
}
SceneItemSelection s;
s._sceneItem = _sceneItem;
if (_showAll && idx == 0) {
if (_allType == AllSelectionType::ALL) {
s._target = SceneItemSelection::Target::ALL;
} else {
s._target = SceneItemSelection::Target::ANY;
}
s._idx = 0;
} else {
s._target = SceneItemSelection::Target::INDIVIDUAL;
if (_showAll) {
idx -= 1;
}
s._idx = idx;
}
emit SceneItemChanged(s);
}
void SceneItemSelectionWidget::SetupIdxSelection(int sceneItemCount)
{
_idx->clear();
if (_showAll) {
if (_allType == AllSelectionType::ALL) {
_idx->addItem(obs_module_text(
"AdvSceneSwitcher.sceneItemSelection.all"));
} else {
_idx->addItem(obs_module_text(
"AdvSceneSwitcher.sceneItemSelection.any"));
}
}
for (int i = 1; i <= sceneItemCount; ++i) {
_idx->addItem(QString::number(i) + ".");
}
adjustSize();
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include <QComboBox>
#include <obs-data.h>
#include "scene-selection.hpp"
#include "utility.hpp"
class SceneItemSelection {
public:
void Save(obs_data_t *obj, const char *name = "sceneItem",
const char *targetName = "sceneItemTarget",
const char *idxName = "sceneItemIdx");
void Load(obs_data_t *obj, const char *name = "sceneItem",
const char *targetName = "sceneItemTarget",
const char *idxName = "sceneItemIdx");
enum class Target { ALL, ANY, INDIVIDUAL };
Target GetType() { return _target; }
std::vector<obs_scene_item *> GetSceneItems(SceneSelection &s);
std::string ToString();
private:
OBSWeakSource _sceneItem;
Target _target = Target::ALL;
int _idx = 0;
friend class SceneItemSelectionWidget;
};
class SceneItemSelectionWidget : public QWidget {
Q_OBJECT
public:
enum class AllSelectionType { ALL, ANY };
SceneItemSelectionWidget(
QWidget *parent, bool allSelection = true,
AllSelectionType allType = AllSelectionType::ALL);
void SetSceneItem(const SceneItemSelection &);
void SetScene(const SceneSelection &);
void SetShowAll(bool);
void SetShowAllSelectionType(AllSelectionType t);
signals:
void SceneItemChanged(const SceneItemSelection &);
private slots:
void SceneChanged(const SceneSelection &);
void SelectionChanged(const QString &name);
void IdxChanged(int);
private:
void SetupIdxSelection(int);
QComboBox *_sceneItems;
QComboBox *_idx;
SceneSelection _scene;
OBSWeakSource _sceneItem;
bool _showAll = false;
AllSelectionType _allType = AllSelectionType::ALL;
};

View File

@@ -0,0 +1,252 @@
#include "scene-selection.hpp"
#include "advanced-scene-switcher.hpp"
void SceneSelection::Save(obs_data_t *obj, const char *name,
const char *typeName)
{
obs_data_set_int(obj, typeName, static_cast<int>(_type));
switch (_type) {
case SceneSelectionType::SCENE:
obs_data_set_string(obj, name,
GetWeakSourceName(_scene).c_str());
break;
case SceneSelectionType::GROUP:
obs_data_set_string(obj, name, _group->name.c_str());
break;
default:
break;
}
}
void SceneSelection::Load(obs_data_t *obj, const char *name,
const char *typeName)
{
_type = static_cast<SceneSelectionType>(
obs_data_get_int(obj, typeName));
auto target = obs_data_get_string(obj, name);
switch (_type) {
case SceneSelectionType::SCENE:
_scene = GetWeakSourceByName(target);
break;
case SceneSelectionType::GROUP:
_group = GetSceneGroupByName(target);
break;
case SceneSelectionType::PREVIOUS:
break;
case SceneSelectionType::CURRENT:
break;
default:
break;
}
}
OBSWeakSource SceneSelection::GetScene(bool advance)
{
switch (_type) {
case SceneSelectionType::SCENE:
return _scene;
case SceneSelectionType::GROUP:
if (!_group) {
return nullptr;
}
if (advance) {
return _group->getNextScene();
}
return _group->getCurrentScene();
case SceneSelectionType::PREVIOUS:
return switcher->previousScene;
case SceneSelectionType::CURRENT:
return switcher->currentScene;
default:
break;
}
return nullptr;
}
std::string SceneSelection::ToString()
{
switch (_type) {
case SceneSelectionType::SCENE:
return GetWeakSourceName(_scene);
case SceneSelectionType::GROUP:
if (_group) {
return _group->name;
}
break;
case SceneSelectionType::PREVIOUS:
return obs_module_text("AdvSceneSwitcher.selectPreviousScene");
case SceneSelectionType::CURRENT:
return obs_module_text("AdvSceneSwitcher.selectCurrentScene");
default:
break;
}
return "";
}
SceneSelectionWidget::SceneSelectionWidget(QWidget *parent, bool sceneGroups,
bool previous, bool current)
: QComboBox(parent)
{
// For the rare occasion of a name conflict with current / previous
setDuplicatesEnabled(true);
populateSceneSelection(this, previous, current, false, sceneGroups,
&switcher->sceneGroups);
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SelectionChanged(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupAdded(const QString &)), this,
SLOT(SceneGroupAdd(const QString &)));
QWidget::connect(parent, SIGNAL(SceneGroupRemoved(const QString &)),
this, SLOT(SceneGroupRemove(const QString &)));
QWidget::connect(
parent,
SIGNAL(SceneGroupRenamed(const QString &, const QString &)),
this, SLOT(SceneGroupRename(const QString &, const QString &)));
}
void SceneSelectionWidget::SetScene(SceneSelection &s)
{
// Order of entries
// 1. Any Scene (current not used)
// 2. Current Scene
// 3. Previous Scene
// 4. Scenes / Scene Groups
int idx;
switch (s.GetType()) {
case SceneSelectionType::SCENE:
case SceneSelectionType::GROUP:
setCurrentText(QString::fromStdString(s.ToString()));
break;
case SceneSelectionType::PREVIOUS:
idx = findText(QString::fromStdString(obs_module_text(
"AdvSceneSwitcher.selectPreviousScene")));
if (idx != -1) {
setCurrentIndex(idx);
}
break;
case SceneSelectionType::CURRENT:
idx = findText(QString::fromStdString(obs_module_text(
"AdvSceneSwitcher.selectCurrentScene")));
if (idx != -1) {
setCurrentIndex(idx);
}
break;
default:
setCurrentIndex(0);
break;
}
}
static bool isFirstEntry(QComboBox *l, QString name, int idx)
{
for (auto i = l->count() - 1; i >= 0; i--) {
if (l->itemText(i) == name) {
return idx == i;
}
}
// If entry cannot be found we dont want the selection to be empty
return false;
}
bool SceneSelectionWidget::IsCurrentSceneSelected(const QString &name)
{
if (name == QString::fromStdString((obs_module_text(
"AdvSceneSwitcher.selectCurrentScene")))) {
return isFirstEntry(this, name, currentIndex());
}
return false;
}
bool SceneSelectionWidget::IsPreviousSceneSelected(const QString &name)
{
if (name == QString::fromStdString((obs_module_text(
"AdvSceneSwitcher.selectPreviousScene")))) {
return isFirstEntry(this, name, currentIndex());
}
return false;
}
void SceneSelectionWidget::SelectionChanged(const QString &name)
{
SceneSelection s;
auto scene = GetWeakSourceByQString(name);
if (scene) {
s._type = SceneSelectionType::SCENE;
s._scene = scene;
}
auto group = GetSceneGroupByQString(name);
if (group) {
s._type = SceneSelectionType::GROUP;
s._scene = nullptr;
s._group = group;
}
if (!scene && !group) {
if (IsCurrentSceneSelected(name)) {
s._type = SceneSelectionType::CURRENT;
}
if (IsPreviousSceneSelected(name)) {
s._type = SceneSelectionType::PREVIOUS;
}
}
emit SceneChanged(s);
}
void SceneSelectionWidget::SceneGroupAdd(const QString &name)
{
addItem(name);
}
void SceneSelectionWidget::SceneGroupRemove(const QString &name)
{
int idx = findText(name);
if (idx == -1) {
return;
}
int curIdx = currentIndex();
removeItem(idx);
if (curIdx == idx) {
SceneSelection s;
emit SceneChanged(s);
}
setCurrentIndex(0);
}
static int findLastOf(QComboBox *l, QString name)
{
int idx = 0;
for (auto i = l->count() - 1; i >= 0; i--) {
if (l->itemText(i) == name) {
return idx;
}
}
return idx;
}
void SceneSelectionWidget::SceneGroupRename(const QString &oldName,
const QString &newName)
{
bool renameSelected = currentText() == oldName;
int idx = findText(oldName);
if (idx == -1) {
return;
}
removeItem(idx);
insertItem(idx, newName);
if (renameSelected) {
setCurrentIndex(findLastOf(this, newName));
}
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include "scene-group.hpp"
#include "utility.hpp"
#include <QComboBox>
enum class SceneSelectionType {
SCENE,
GROUP,
PREVIOUS,
CURRENT,
};
class SceneSelection {
public:
void Save(obs_data_t *obj, const char *name = "scene",
const char *typeName = "sceneType");
void Load(obs_data_t *obj, const char *name = "scene",
const char *typeName = "sceneType");
SceneSelectionType GetType() { return _type; }
OBSWeakSource GetScene(bool advance = true);
std::string ToString();
private:
OBSWeakSource _scene;
SceneGroup *_group = nullptr;
SceneSelectionType _type = SceneSelectionType::SCENE;
friend class SceneSelectionWidget;
};
class SceneSelectionWidget : public QComboBox {
Q_OBJECT
public:
SceneSelectionWidget(QWidget *parent, bool sceneGroups = false,
bool previous = false, bool current = false);
void SetScene(SceneSelection &);
signals:
void SceneChanged(const SceneSelection &);
private slots:
void SelectionChanged(const QString &name);
void SceneGroupAdd(const QString &name);
void SceneGroupRemove(const QString &name);
void SceneGroupRename(const QString &oldName, const QString &newName);
private:
bool IsCurrentSceneSelected(const QString &name);
bool IsPreviousSceneSelected(const QString &name);
};

View File

@@ -0,0 +1,136 @@
#include "screenshot-helper.hpp"
#include "advanced-scene-switcher.hpp"
static void ScreenshotTick(void *param, float);
ScreenshotHelper::ScreenshotHelper(obs_source_t *source)
: weakSource(OBSGetWeakRef(source))
{
_initDone = true;
obs_add_tick_callback(ScreenshotTick, this);
}
ScreenshotHelper::~ScreenshotHelper()
{
if (_initDone) {
obs_enter_graphics();
gs_stagesurface_destroy(stagesurf);
gs_texrender_destroy(texrender);
obs_leave_graphics();
obs_remove_tick_callback(ScreenshotTick, this);
}
}
void ScreenshotHelper::Screenshot()
{
OBSSource source = OBSGetStrongRef(weakSource);
if (source) {
cx = obs_source_get_base_width(source);
cy = obs_source_get_base_height(source);
} else {
obs_video_info ovi;
obs_get_video_info(&ovi);
cx = ovi.base_width;
cy = ovi.base_height;
}
if (!cx || !cy) {
vblog(LOG_WARNING,
"Cannot screenshot \"%s\", invalid target size",
obs_source_get_name(source));
obs_remove_tick_callback(ScreenshotTick, this);
done = true;
return;
}
texrender = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
stagesurf = gs_stagesurface_create(cx, cy, GS_RGBA);
gs_texrender_reset(texrender);
if (gs_texrender_begin(texrender, cx, cy)) {
vec4 zero;
vec4_zero(&zero);
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
gs_blend_state_push();
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
if (source) {
obs_source_inc_showing(source);
obs_source_video_render(source);
obs_source_dec_showing(source);
} else {
obs_render_main_texture();
}
gs_blend_state_pop();
gs_texrender_end(texrender);
}
}
void ScreenshotHelper::Download()
{
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
}
void ScreenshotHelper::Copy()
{
uint8_t *videoData = nullptr;
uint32_t videoLinesize = 0;
image = QImage(cx, cy, QImage::Format::Format_RGBA8888);
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
int linesize = image.bytesPerLine();
for (int y = 0; y < (int)cy; y++)
memcpy(image.scanLine(y),
videoData + (y * videoLinesize), linesize);
gs_stagesurface_unmap(stagesurf);
}
}
void ScreenshotHelper::MarkDone()
{
time = std::chrono::high_resolution_clock::now();
done = true;
}
#define STAGE_SCREENSHOT 0
#define STAGE_DOWNLOAD 1
#define STAGE_COPY_AND_SAVE 2
#define STAGE_FINISH 3
static void ScreenshotTick(void *param, float)
{
ScreenshotHelper *data = reinterpret_cast<ScreenshotHelper *>(param);
if (data->stage == STAGE_FINISH) {
return;
}
obs_enter_graphics();
switch (data->stage) {
case STAGE_SCREENSHOT:
data->Screenshot();
break;
case STAGE_DOWNLOAD:
data->Download();
break;
case STAGE_COPY_AND_SAVE:
data->Copy();
data->MarkDone();
obs_remove_tick_callback(ScreenshotTick, data);
break;
}
obs_leave_graphics();
data->stage++;
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <obs.hpp>
#include <string>
#include <QImage>
#include <chrono>
class ScreenshotHelper {
public:
ScreenshotHelper() = default;
ScreenshotHelper(obs_source_t *source);
ScreenshotHelper &operator=(const ScreenshotHelper &) = delete;
ScreenshotHelper(const ScreenshotHelper &) = delete;
~ScreenshotHelper();
void Screenshot();
void Download();
void Copy();
void MarkDone();
gs_texrender_t *texrender = nullptr;
gs_stagesurf_t *stagesurf = nullptr;
OBSWeakSource weakSource;
QImage image;
uint32_t cx = 0;
uint32_t cy = 0;
int stage = 0;
bool done = false;
std::chrono::high_resolution_clock::time_point time;
private:
bool _initDone = false;
};

176
src/utils/section.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include "section.hpp"
#include "utility.hpp"
#include <QPropertyAnimation>
#include <QEvent>
Section::Section(const int animationDuration, QWidget *parent)
: QWidget(parent), _animationDuration(animationDuration)
{
_toggleButton = new QToolButton(this);
_headerLine = new QFrame(this);
_mainLayout = new QGridLayout(this);
_headerWidgetLayout = new QHBoxLayout();
_toggleButton->setStyleSheet(
"QToolButton {border: none; background-color: rgba(0,0,0,0);}");
_toggleButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
_toggleButton->setArrowType(Qt::ArrowType::RightArrow);
_toggleButton->setCheckable(true);
_toggleButton->setChecked(true);
_headerLine->setFrameShape(QFrame::HLine);
_headerLine->setFrameShadow(QFrame::Sunken);
_headerLine->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Maximum);
// Don't waste space
_mainLayout->setVerticalSpacing(0);
_mainLayout->setContentsMargins(0, 0, 0, 0);
// But add some spacing for widgets in header
_headerWidgetLayout->setSpacing(11);
_headerWidgetLayout->addWidget(_toggleButton);
_mainLayout->addLayout(_headerWidgetLayout, 0, 0, 1, 1, Qt::AlignLeft);
_mainLayout->addWidget(_headerLine, 0, 2, 1, 1);
setLayout(_mainLayout);
connect(_toggleButton, &QToolButton::toggled, this, &Section::Collapse);
}
void Section::Collapse(bool collapse)
{
_toggleButton->setChecked(collapse);
_toggleButton->setArrowType(collapse ? Qt::ArrowType::RightArrow
: Qt::ArrowType::DownArrow);
_toggleAnimation->setDirection(collapse ? QAbstractAnimation::Backward
: QAbstractAnimation::Forward);
_transitioning = true;
_collapsed = collapse;
_toggleAnimation->start();
emit Collapsed(collapse);
}
void Section::SetContent(QWidget *w)
{
SetContent(w, _collapsed);
}
void Section::SetContent(QWidget *w, bool collapsed)
{
CleanUpPreviousContent();
delete _contentArea;
// Setup contentArea
_contentArea = new QScrollArea(this);
_contentArea->setObjectName("macroSegmentContent");
_contentArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
_contentArea->setStyleSheet(
"#macroSegmentContent { border: none; background-color: rgba(0,0,0,0); }");
_contentArea->setMaximumHeight(0);
_contentArea->setMinimumHeight(0);
_content = w;
_content->installEventFilter(this);
auto newLayout = new QVBoxLayout();
newLayout->setContentsMargins(0, 0, 0, 0);
newLayout->addWidget(w);
_contentArea->setLayout(newLayout);
_mainLayout->addWidget(_contentArea, 1, 0, 1, 3);
_headerHeight = sizeHint().height() - _contentArea->maximumHeight();
_contentHeight = _content->sizeHint().height();
SetupAnimations();
if (collapsed) {
this->setMinimumHeight(_headerHeight);
_contentArea->setMaximumHeight(0);
} else {
this->setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
}
const QSignalBlocker b(_toggleButton);
_toggleButton->setChecked(collapsed);
_toggleButton->setArrowType(collapsed ? Qt::ArrowType::RightArrow
: Qt::ArrowType::DownArrow);
_collapsed = collapsed;
}
void Section::AddHeaderWidget(QWidget *w)
{
_headerWidgetLayout->addWidget(w);
}
void Section::SetCollapsed(bool collapsed)
{
if (_collapsed == collapsed) {
return;
}
Collapse(collapsed);
}
bool Section::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Resize && !_transitioning && !_collapsed) {
if (_contentHeight != _content->sizeHint().height()) {
_contentHeight = _content->sizeHint().height();
setMaximumHeight(_headerHeight + _contentHeight);
setMinimumHeight(_headerHeight + _contentHeight);
_contentArea->setMaximumHeight(_contentHeight);
// Note: Calling this too frequently inside this event
// filter will cause a segfault for some reason
SetupAnimations();
}
}
return QObject::eventFilter(obj, event);
}
void Section::SetupAnimations()
{
delete _toggleAnimation;
_toggleAnimation = new QParallelAnimationGroup(this);
_toggleAnimation->addAnimation(
new QPropertyAnimation(this, "minimumHeight"));
_toggleAnimation->addAnimation(
new QPropertyAnimation(this, "maximumHeight"));
_toggleAnimation->addAnimation(
new QPropertyAnimation(_contentArea, "maximumHeight"));
for (int i = 0; i < _toggleAnimation->animationCount() - 1; ++i) {
QPropertyAnimation *SectionAnimation =
static_cast<QPropertyAnimation *>(
_toggleAnimation->animationAt(i));
SectionAnimation->setDuration(_animationDuration);
SectionAnimation->setStartValue(_headerHeight);
SectionAnimation->setEndValue(_headerHeight + _contentHeight);
}
QPropertyAnimation *contentAnimation =
static_cast<QPropertyAnimation *>(_toggleAnimation->animationAt(
_toggleAnimation->animationCount() - 1));
contentAnimation->setDuration(_animationDuration);
contentAnimation->setStartValue(0);
contentAnimation->setEndValue(_contentHeight);
QWidget::connect(_toggleAnimation, SIGNAL(finished()), this,
SLOT(AnimationFinished()));
}
void Section::CleanUpPreviousContent()
{
if (_contentArea) {
auto oldLayout = _contentArea->layout();
if (oldLayout) {
clearLayout(oldLayout);
delete oldLayout;
}
}
}
void Section::AnimationFinished()
{
_transitioning = false;
}

48
src/utils/section.hpp Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <QFrame>
#include <QGridLayout>
#include <QParallelAnimationGroup>
#include <QScrollArea>
#include <QToolButton>
#include <QWidget>
class Section : public QWidget {
Q_OBJECT
public:
explicit Section(const int animationDuration = 300,
QWidget *parent = 0);
void SetContent(QWidget *w);
void SetContent(QWidget *w, bool collapsed);
void AddHeaderWidget(QWidget *);
void SetCollapsed(bool);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void AnimationFinished();
void Collapse(bool collapse);
signals:
void Collapsed(bool);
private:
void SetupAnimations();
void CleanUpPreviousContent();
QGridLayout *_mainLayout;
QHBoxLayout *_headerWidgetLayout;
QToolButton *_toggleButton;
QFrame *_headerLine;
QParallelAnimationGroup *_toggleAnimation = nullptr;
QParallelAnimationGroup *_contentAnimation = nullptr;
QScrollArea *_contentArea = nullptr;
QWidget *_content = nullptr;
int _animationDuration;
std::atomic_bool _transitioning = {false};
std::atomic_bool _collapsed = {false};
int _headerHeight = 0;
int _contentHeight = 0;
};

View File

@@ -0,0 +1,172 @@
#include "transition-selection.hpp"
#include "advanced-scene-switcher.hpp"
void TransitionSelection::Save(obs_data_t *obj, const char *name,
const char *typeName)
{
obs_data_set_int(obj, typeName, static_cast<int>(_type));
switch (_type) {
case TransitionSelectionType::TRANSITION:
obs_data_set_string(obj, name,
GetWeakSourceName(_transition).c_str());
break;
default:
break;
}
}
void TransitionSelection::Load(obs_data_t *obj, const char *name,
const char *typeName)
{
_type = static_cast<TransitionSelectionType>(
obs_data_get_int(obj, typeName));
auto target = obs_data_get_string(obj, name);
switch (_type) {
case TransitionSelectionType::TRANSITION:
_transition = GetWeakTransitionByName(target);
break;
default:
break;
}
}
OBSWeakSource TransitionSelection::GetTransition()
{
switch (_type) {
case TransitionSelectionType::TRANSITION:
return _transition;
case TransitionSelectionType::CURRENT: {
auto source = obs_frontend_get_current_transition();
auto weakSource = obs_source_get_weak_source(source);
obs_weak_source_release(weakSource);
obs_source_release(source);
return weakSource;
}
default:
break;
}
return nullptr;
}
std::string TransitionSelection::ToString()
{
switch (_type) {
case TransitionSelectionType::TRANSITION:
return GetWeakSourceName(_transition);
case TransitionSelectionType::CURRENT:
return obs_module_text("AdvSceneSwitcher.currentTransition");
case TransitionSelectionType::ANY:
return obs_module_text("AdvSceneSwitcher.anyTransition");
default:
break;
}
return "";
}
TransitionSelectionWidget::TransitionSelectionWidget(QWidget *parent,
bool current, bool any)
: QComboBox(parent)
{
setDuplicatesEnabled(true);
populateTransitionSelection(this, current, any);
QWidget::connect(this, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(SelectionChanged(const QString &)));
}
void TransitionSelectionWidget::SetTransition(TransitionSelection &t)
{
// Order of entries
// 1. Any transition
// 2. Current transition
// 4. Transitions
int idx;
switch (t.GetType()) {
case TransitionSelectionType::TRANSITION:
setCurrentText(QString::fromStdString(t.ToString()));
break;
case TransitionSelectionType::CURRENT:
idx = findText(QString::fromStdString(
obs_module_text("AdvSceneSwitcher.currentTransition")));
if (idx != -1) {
setCurrentIndex(idx);
}
break;
case TransitionSelectionType::ANY:
idx = findText(QString::fromStdString(
obs_module_text("AdvSceneSwitcher.anyTransition")));
if (idx != -1) {
setCurrentIndex(idx);
}
break;
default:
setCurrentIndex(0);
break;
}
}
void TransitionSelectionWidget::Repopulate(bool current, bool any)
{
{
const QSignalBlocker blocker(this);
clear();
populateTransitionSelection(this, current, any);
setCurrentIndex(0);
}
TransitionSelection t;
emit TransitionChanged(t);
}
static bool isFirstEntry(QComboBox *l, QString name, int idx)
{
for (auto i = l->count() - 1; i >= 0; i--) {
if (l->itemText(i) == name) {
return idx == i;
}
}
// If entry cannot be found we dont want the selection to be empty
return false;
}
bool TransitionSelectionWidget::IsCurrentTransitionSelected(const QString &name)
{
if (name == QString::fromStdString((obs_module_text(
"AdvSceneSwitcher.currentTransition")))) {
return isFirstEntry(this, name, currentIndex());
}
return false;
}
bool TransitionSelectionWidget::IsAnyTransitionSelected(const QString &name)
{
if (name == QString::fromStdString((obs_module_text(
"AdvSceneSwitcher.anyTransition")))) {
return isFirstEntry(this, name, currentIndex());
}
return false;
}
void TransitionSelectionWidget::SelectionChanged(const QString &name)
{
TransitionSelection t;
auto transition = GetWeakTransitionByQString(name);
if (transition) {
t._type = TransitionSelectionType::TRANSITION;
t._transition = transition;
}
if (!transition) {
if (IsCurrentTransitionSelected(name)) {
t._type = TransitionSelectionType::CURRENT;
}
if (IsAnyTransitionSelected(name)) {
t._type = TransitionSelectionType::ANY;
}
}
emit TransitionChanged(t);
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "utility.hpp"
#include <QComboBox>
enum class TransitionSelectionType {
TRANSITION,
CURRENT,
ANY,
};
class TransitionSelection {
public:
void Save(obs_data_t *obj, const char *name = "transition",
const char *typeName = "transitionType");
void Load(obs_data_t *obj, const char *name = "transition",
const char *typeName = "transitionType");
TransitionSelectionType GetType() { return _type; }
OBSWeakSource GetTransition();
std::string ToString();
private:
OBSWeakSource _transition;
TransitionSelectionType _type = TransitionSelectionType::TRANSITION;
friend class TransitionSelectionWidget;
};
class TransitionSelectionWidget : public QComboBox {
Q_OBJECT
public:
TransitionSelectionWidget(QWidget *parent, bool current = true,
bool any = false);
void SetTransition(TransitionSelection &);
void Repopulate(bool current, bool any);
signals:
void TransitionChanged(const TransitionSelection &);
private slots:
void SelectionChanged(const QString &name);
private:
bool IsCurrentTransitionSelected(const QString &name);
bool IsAnyTransitionSelected(const QString &name);
};

1022
src/utils/utility.cpp Normal file

File diff suppressed because it is too large Load Diff

86
src/utils/utility.hpp Normal file
View File

@@ -0,0 +1,86 @@
#pragma once
#include <QString>
#include <QLayout>
#include <QComboBox>
#include <QMetaObject>
#include <QListWidget>
#include <QPushButton>
#include <QColor>
#include <obs.hpp>
#include <obs-frontend-api.h>
#include <deque>
#include <unordered_map>
#include "scene-group.hpp"
class SceneSelection;
bool WeakSourceValid(obs_weak_source_t *ws);
std::string GetWeakSourceName(obs_weak_source_t *weak_source);
OBSWeakSource GetWeakSourceByName(const char *name);
OBSWeakSource GetWeakSourceByQString(const QString &name);
OBSWeakSource GetWeakTransitionByName(const char *transitionName);
OBSWeakSource GetWeakTransitionByQString(const QString &name);
OBSWeakSource GetWeakFilterByName(OBSWeakSource source, const char *name);
OBSWeakSource GetWeakFilterByQString(OBSWeakSource source, const QString &name);
bool compareIgnoringLineEnding(QString &s1, QString &s2);
std::string getSourceSettings(OBSWeakSource ws);
void setSourceSettings(obs_source_t *s, const std::string &settings);
bool compareSourceSettings(const OBSWeakSource &source,
const std::string &settings, bool regex);
std::vector<obs_scene_item *> getSceneItemsWithName(obs_scene_t *scene,
std::string &name);
std::string getDataFilePath(const std::string &file);
bool matchJson(const std::string &json1, const std::string &json2,
bool useRegex);
QString formatJsonString(std::string);
QString formatJsonString(QString);
QString escapeForRegex(QString &s);
void loadTransformState(obs_data_t *obj, struct obs_transform_info &info,
struct obs_sceneitem_crop &crop);
bool saveTransformState(obs_data_t *obj, struct obs_transform_info &info,
struct obs_sceneitem_crop &crop);
std::string getSceneItemTransform(obs_scene_item *item);
void placeWidgets(std::string text, QBoxLayout *layout,
std::unordered_map<std::string, QWidget *> placeholders,
bool addStretch = true);
void deleteLayoutItemWidget(QLayoutItem *item);
void clearLayout(QLayout *layout, int afterIdx = 0);
void setLayoutVisible(QLayout *layout, bool visible);
QMetaObject::Connection PulseWidget(QWidget *widget, QColor startColor,
QColor endColor = QColor(0, 0, 0, 0),
bool once = false);
void listAddClicked(QListWidget *list, QWidget *newWidget,
QPushButton *addButton = nullptr,
QMetaObject::Connection *addHighlight = nullptr);
bool listMoveUp(QListWidget *list);
bool listMoveDown(QListWidget *list);
void setHeightToContentHeight(QListWidget *list);
bool DisplayMessage(const QString &msg, bool question = false);
void DisplayTrayMessage(const QString &title, const QString &msg);
void addSelectionEntry(QComboBox *sel, const char *description,
bool selectable = false, const char *tooltip = "");
void populateTransitionSelection(QComboBox *sel, bool addCurrent = true,
bool addAny = false);
void populateWindowSelection(QComboBox *sel, bool addSelect = true);
void populateAudioSelection(QComboBox *sel, bool addSelect = true);
void populateVideoSelection(QComboBox *sel, bool addMainOutput = false,
bool addScenes = false, bool addSelect = true);
void populateMediaSelection(QComboBox *sel, bool addSelect = true);
void populateProcessSelection(QComboBox *sel, bool addSelect = true);
void populateSourceSelection(QComboBox *list, bool addSelect = true);
void populateSceneSelection(QComboBox *sel, bool addPrevious = false,
bool addCurrent = false, bool addAny = false,
bool addSceneGroup = false,
std::deque<SceneGroup> *sceneGroups = nullptr,
bool addSelect = true, std::string selectText = "",
bool selectable = false);
void populateSourcesWithFilterSelection(QComboBox *list);
void populateFilterSelection(QComboBox *list,
OBSWeakSource weakSource = nullptr);
void populateSceneItemSelection(QComboBox *list,
OBSWeakSource sceneWeakSource = nullptr);
void populateSceneItemSelection(QComboBox *list, SceneSelection &s);
void populateSourceGroupSelection(QComboBox *list);
void populateProfileSelection(QComboBox *list);
bool windowPosValid(QPoint pos);
bool doubleEquals(double left, double right, double epsilon);

1005
src/utils/volume-control.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
#pragma once
#include <obs.hpp>
#include <QWidget>
#include <QPaintEvent>
#include <QSharedPointer>
#include <QTimer>
#include <QMutex>
#include <QList>
class QPushButton;
class VolumeMeterTimer;
class VolumeMeter : public QWidget {
Q_OBJECT
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor
WRITE setBackgroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor
WRITE setBackgroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor backgroundErrorColor READ getBackgroundErrorColor
WRITE setBackgroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor
WRITE setForegroundNominalColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor
WRITE setForegroundWarningColor DESIGNABLE true)
Q_PROPERTY(QColor foregroundErrorColor READ getForegroundErrorColor
WRITE setForegroundErrorColor DESIGNABLE true)
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor
DESIGNABLE true)
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE
setMagnitudeColor DESIGNABLE true)
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE
setMajorTickColor DESIGNABLE true)
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE
setMinorTickColor DESIGNABLE true)
// Levels are denoted in dBFS.
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel
DESIGNABLE true)
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel
DESIGNABLE true)
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel
DESIGNABLE true)
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel
DESIGNABLE true)
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE
setMinimumInputLevel DESIGNABLE true)
// Rates are denoted in dB/second.
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE
setPeakDecayRate DESIGNABLE true)
// Time in seconds for the VU meter to integrate over.
Q_PROPERTY(
qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime
WRITE setMagnitudeIntegrationTime DESIGNABLE true)
// Duration is denoted in seconds.
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE
setPeakHoldDuration DESIGNABLE true)
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration
WRITE setInputPeakHoldDuration DESIGNABLE true)
private slots:
void ClipEnding();
private:
obs_volmeter_t *obs_volmeter;
static QWeakPointer<VolumeMeterTimer> updateTimer;
QSharedPointer<VolumeMeterTimer> updateTimerRef;
inline void resetLevels();
inline void handleChannelCofigurationChange();
inline bool detectIdle(uint64_t ts);
inline void calculateBallistics(uint64_t ts,
qreal timeSinceLastRedraw = 0.0);
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts,
qreal timeSinceLastRedraw);
void paintInputMeter(QPainter &painter, int x, int y, int width,
int height, float peakHold);
void paintHMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintHTicks(QPainter &painter, int x, int y, int width,
int height);
void paintVMeter(QPainter &painter, int x, int y, int width, int height,
float magnitude, float peak, float peakHold);
void paintVTicks(QPainter &painter, int x, int y, int height);
QMutex dataMutex;
uint64_t currentLastUpdateTime = 0;
float currentMagnitude[MAX_AUDIO_CHANNELS];
float currentPeak[MAX_AUDIO_CHANNELS];
float currentInputPeak[MAX_AUDIO_CHANNELS];
QPixmap *tickPaintCache = nullptr;
int displayNrAudioChannels = 0;
float displayMagnitude[MAX_AUDIO_CHANNELS];
float displayPeak[MAX_AUDIO_CHANNELS];
float displayPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
QFont tickFont;
QColor backgroundNominalColor;
QColor backgroundWarningColor;
QColor backgroundErrorColor;
QColor foregroundNominalColor;
QColor foregroundWarningColor;
QColor foregroundErrorColor;
QColor clipColor;
QColor magnitudeColor;
QColor majorTickColor;
QColor minorTickColor;
qreal minimumLevel;
qreal warningLevel;
qreal errorLevel;
qreal clipLevel;
qreal minimumInputLevel;
qreal peakDecayRate;
qreal magnitudeIntegrationTime;
qreal peakHoldDuration;
qreal inputPeakHoldDuration;
uint64_t lastRedrawTime = 0;
int channels = 0;
bool clipping = false;
bool vertical;
public:
explicit VolumeMeter(QWidget *parent = nullptr,
obs_volmeter_t *obs_volmeter = nullptr,
bool vertical = false);
~VolumeMeter();
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
QColor getBackgroundNominalColor() const;
void setBackgroundNominalColor(QColor c);
QColor getBackgroundWarningColor() const;
void setBackgroundWarningColor(QColor c);
QColor getBackgroundErrorColor() const;
void setBackgroundErrorColor(QColor c);
QColor getForegroundNominalColor() const;
void setForegroundNominalColor(QColor c);
QColor getForegroundWarningColor() const;
void setForegroundWarningColor(QColor c);
QColor getForegroundErrorColor() const;
void setForegroundErrorColor(QColor c);
QColor getClipColor() const;
void setClipColor(QColor c);
QColor getMagnitudeColor() const;
void setMagnitudeColor(QColor c);
QColor getMajorTickColor() const;
void setMajorTickColor(QColor c);
QColor getMinorTickColor() const;
void setMinorTickColor(QColor c);
qreal getMinimumLevel() const;
void setMinimumLevel(qreal v);
qreal getWarningLevel() const;
void setWarningLevel(qreal v);
qreal getErrorLevel() const;
void setErrorLevel(qreal v);
qreal getClipLevel() const;
void setClipLevel(qreal v);
qreal getMinimumInputLevel() const;
void setMinimumInputLevel(qreal v);
qreal getPeakDecayRate() const;
void setPeakDecayRate(qreal v);
qreal getMagnitudeIntegrationTime() const;
void setMagnitudeIntegrationTime(qreal v);
qreal getPeakHoldDuration() const;
void setPeakHoldDuration(qreal v);
qreal getInputPeakHoldDuration() const;
void setInputPeakHoldDuration(qreal v);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
virtual void mousePressEvent(QMouseEvent *event) override;
protected:
void paintEvent(QPaintEvent *event) override;
};
class VolumeMeterTimer : public QTimer {
Q_OBJECT
public:
inline VolumeMeterTimer() : QTimer() {}
void AddVolControl(VolumeMeter *meter);
void RemoveVolControl(VolumeMeter *meter);
protected:
void timerEvent(QTimerEvent *event) override;
QList<VolumeMeter *> volumeMeters;
};
class QLabel;
class QSlider;
class MuteCheckBox;
class VolControl : public QWidget {
Q_OBJECT
private:
OBSSource source;
QLabel *nameLabel;
QLabel *volLabel;
VolumeMeter *volMeter;
QSlider *slider;
float levelTotal;
float levelCount;
obs_fader_t *obs_fader;
obs_volmeter_t *obs_volmeter;
bool vertical;
static void OBSVolumeChanged(void *param, float db);
static void OBSVolumeLevel(void *data,
const float magnitude[MAX_AUDIO_CHANNELS],
const float peak[MAX_AUDIO_CHANNELS],
const float inputPeak[MAX_AUDIO_CHANNELS]);
private slots:
void SliderChanged(int vol);
void updateText();
public:
explicit VolControl(OBSSource source, bool vertical = false);
~VolControl();
inline obs_source_t *GetSource() const { return source; }
QString GetName() const;
void SetName(const QString &newName);
void SetMeterDecayRate(qreal q);
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
void EnableSlider(bool enable);
QSlider *GetSlider() const;
};