Add helper classes to support strings containing variables

* VariableResolvingString will accept strings potentially containing
   variables as inputs and will automatically resolve any variables when
   converting to std::string
 * VariableTextEdit is a simple wrapper around ResizingPlainTextEdit to
   enable working with VariableResolvingString
This commit is contained in:
WarmUpTill 2022-11-30 20:00:05 +01:00 committed by WarmUpTill
parent 632ad5085f
commit 14f0194372
6 changed files with 161 additions and 3 deletions

View File

@ -270,6 +270,8 @@ target_sources(
src/utils/utility.hpp
src/utils/variable.cpp
src/utils/variable.hpp
src/utils/variable-text-edit.cpp
src/utils/variable-text-edit.hpp
src/utils/volume-control.cpp
src/utils/volume-control.hpp
src/utils/websocket-helpers.cpp

View File

@ -7,6 +7,7 @@ public:
ResizingPlainTextEdit(QWidget *parent, const int scrollAt = 10,
const int minLines = 3,
const int paddingLines = 2);
virtual ~ResizingPlainTextEdit(){};
private slots:
void ResizeTexteditArea();

View File

@ -0,0 +1,18 @@
#include "variable-text-edit.hpp"
#include "switcher-data-structs.hpp"
VariableTextEdit::VariableTextEdit(QWidget *parent)
: ResizingPlainTextEdit(parent)
{
}
void VariableTextEdit::setPlainText(const QString &string)
{
QPlainTextEdit::setPlainText(string);
}
void VariableTextEdit::setPlainText(const VariableResolvingString &string)
{
QPlainTextEdit::setPlainText(
QString::fromStdString(string.UnresolvedValue()));
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "resizing-text-edit.hpp"
#include "variable.hpp"
class VariableTextEdit : public ResizingPlainTextEdit {
Q_OBJECT
public:
VariableTextEdit(QWidget *parent);
void setPlainText(const QString &);
void setPlainText(const VariableResolvingString &);
private:
};

View File

@ -2,7 +2,19 @@
#include <switcher-data-structs.hpp>
Variable::Variable() : Item() {}
// Keep track of the last time a variable was changed to save some work when
// when resolving strings containing variables
static std::chrono::high_resolution_clock::time_point lastVariableChange{};
Variable::Variable() : Item()
{
lastVariableChange = std::chrono::high_resolution_clock::now();
}
Variable::~Variable()
{
lastVariableChange = std::chrono::high_resolution_clock::now();
}
void Variable::Load(obs_data_t *obj)
{
@ -15,6 +27,7 @@ void Variable::Load(obs_data_t *obj)
} else if (_saveAction == SaveAction::SET_DEFAULT) {
_value = _defaultValue;
}
lastVariableChange = std::chrono::high_resolution_clock::now();
}
void Variable::Save(obs_data_t *obj) const
@ -34,9 +47,16 @@ bool Variable::DoubleValue(double &value) const
return end != _value.c_str() && *end == '\0' && value != HUGE_VAL;
}
void Variable::SetValue(const std::string &val)
{
_value = val;
lastVariableChange = std::chrono::high_resolution_clock::now();
}
void Variable::SetValue(double value)
{
_value = std::to_string(value);
lastVariableChange = std::chrono::high_resolution_clock::now();
}
Variable *GetVariableByName(const std::string &name)
@ -81,6 +101,78 @@ QStringList GetVariablesNameList()
return list;
}
void VariableResolvingString::Resolve()
{
if (_lastResolve == lastVariableChange) {
return;
}
_resolvedValue = SubstitueVariables(_value);
_lastResolve = lastVariableChange;
}
VariableResolvingString::operator std::string()
{
Resolve();
return _resolvedValue;
}
void VariableResolvingString::operator=(std::string value)
{
_value = value;
_lastResolve = {};
}
void VariableResolvingString::operator=(const char *value)
{
_value = value;
_lastResolve = {};
}
void VariableResolvingString::Load(obs_data_t *obj, const char *name)
{
_value = obs_data_get_string(obj, name);
}
void VariableResolvingString::Save(obs_data_t *obj, const char *name) const
{
obs_data_set_string(obj, name, _value.c_str());
}
const char *VariableResolvingString::c_str()
{
Resolve();
return _resolvedValue.c_str();
}
const char *VariableResolvingString::c_str() const
{
// Just assume that the value was previously resolved already
return _resolvedValue.c_str();
}
static void replaceAll(std::string &str, const std::string &from,
const std::string &to)
{
if (from.empty()) {
return;
}
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
std::string SubstitueVariables(std::string str)
{
for (const auto &v : switcher->variables) {
const auto &variable = std::dynamic_pointer_cast<Variable>(v);
const std::string pattern = "${" + variable->Name() + "}";
replaceAll(str, pattern, variable->Value());
}
return str;
}
void SwitcherData::saveVariables(obs_data_t *obj)
{
obs_data_array_t *variablesArray = obs_data_array_create();
@ -194,7 +286,11 @@ bool VariableSettingsDialog::AskForSettings(QWidget *parent, Variable &settings)
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
{
Variable &VariableSettings = dynamic_cast<Variable &>(settings);
return VariableSettingsDialog::AskForSettings(parent, VariableSettings);
if (VariableSettingsDialog::AskForSettings(parent, VariableSettings)) {
lastVariableChange = std::chrono::high_resolution_clock::now();
return true;
}
return false;
}
VariableSelection::VariableSelection(QWidget *parent)

View File

@ -11,11 +11,12 @@ class VariableSettingsDialog;
class Variable : public Item {
public:
Variable();
~Variable();
void Load(obs_data_t *obj);
void Save(obs_data_t *obj) const;
std::string Value() const { return _value; }
bool DoubleValue(double &) const;
void SetValue(const std::string &val) { _value = val; }
void SetValue(const std::string &val);
void SetValue(double);
static std::shared_ptr<Item> Create()
{
@ -37,11 +38,38 @@ private:
friend VariableSettingsDialog;
};
// Helper class which automatically resovles variables contained in strings
// when reading its value as a std::string
class VariableResolvingString {
public:
VariableResolvingString() : _value(""){};
VariableResolvingString(std::string str) : _value(std::move(str)){};
VariableResolvingString(const char *str) : _value(str){};
operator std::string();
void operator=(std::string);
void operator=(const char *value);
const char *c_str();
const char *c_str() const;
const std::string &UnresolvedValue() const { return _value; }
void Load(obs_data_t *obj, const char *name);
void Save(obs_data_t *obj, const char *name) const;
private:
void Resolve();
std::string _value = "";
std::string _resolvedValue = "";
std::chrono::high_resolution_clock::time_point _lastResolve{};
};
Variable *GetVariableByName(const std::string &name);
Variable *GetVariableByQString(const QString &name);
std::weak_ptr<Variable> GetWeakVariableByName(const std::string &name);
std::weak_ptr<Variable> GetWeakVariableByQString(const QString &name);
QStringList GetVariablesNameList();
std::string SubstitueVariables(std::string str);
class VariableSettingsDialog : public ItemSettingsDialog {
Q_OBJECT