From 14f0194372a50089086b0dea2c064a052b314b05 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 30 Nov 2022 20:00:05 +0100 Subject: [PATCH] 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 --- CMakeLists.txt | 2 + src/utils/resizing-text-edit.hpp | 1 + src/utils/variable-text-edit.cpp | 18 ++++++ src/utils/variable-text-edit.hpp | 13 ++++ src/utils/variable.cpp | 100 ++++++++++++++++++++++++++++++- src/utils/variable.hpp | 30 +++++++++- 6 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 src/utils/variable-text-edit.cpp create mode 100644 src/utils/variable-text-edit.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a82e11cb..4a6fb47e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/utils/resizing-text-edit.hpp b/src/utils/resizing-text-edit.hpp index f68457ed..33352179 100644 --- a/src/utils/resizing-text-edit.hpp +++ b/src/utils/resizing-text-edit.hpp @@ -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(); diff --git a/src/utils/variable-text-edit.cpp b/src/utils/variable-text-edit.cpp new file mode 100644 index 00000000..a96e1ba0 --- /dev/null +++ b/src/utils/variable-text-edit.cpp @@ -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())); +} diff --git a/src/utils/variable-text-edit.hpp b/src/utils/variable-text-edit.hpp new file mode 100644 index 00000000..2d0f98a2 --- /dev/null +++ b/src/utils/variable-text-edit.hpp @@ -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: +}; diff --git a/src/utils/variable.cpp b/src/utils/variable.cpp index 0f419f55..757e0dfb 100644 --- a/src/utils/variable.cpp +++ b/src/utils/variable.cpp @@ -2,7 +2,19 @@ #include -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(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(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) diff --git a/src/utils/variable.hpp b/src/utils/variable.hpp index cd3c5271..7fb9bcef 100644 --- a/src/utils/variable.hpp +++ b/src/utils/variable.hpp @@ -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 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 GetWeakVariableByName(const std::string &name); std::weak_ptr GetWeakVariableByQString(const QString &name); QStringList GetVariablesNameList(); +std::string SubstitueVariables(std::string str); class VariableSettingsDialog : public ItemSettingsDialog { Q_OBJECT