From e5b215b236d59afdd3e2866aba405a4fe3139db9 Mon Sep 17 00:00:00 2001 From: codereader Date: Sun, 17 Dec 2017 12:27:18 +0100 Subject: [PATCH] Implement change signal for GUI expressions to notify windowDef variables and let them recompile their texts. --- include/igui.h | 39 ++++++++++++++++ plugins/dm.gui/gui/Gui.cpp | 24 +++++++++- plugins/dm.gui/gui/Gui.h | 9 +++- plugins/dm.gui/gui/GuiExpression.cpp | 61 +++++++++++++++++++++++-- plugins/dm.gui/gui/GuiExpression.h | 68 ++++++++++++++++++++++++++-- plugins/dm.gui/gui/GuiScript.cpp | 2 +- plugins/dm.gui/gui/GuiWindowDef.cpp | 2 +- 7 files changed, 192 insertions(+), 13 deletions(-) diff --git a/include/igui.h b/include/igui.h index 4a17e189e9..5f3280b828 100644 --- a/include/igui.h +++ b/include/igui.h @@ -6,6 +6,7 @@ #include #include +#include #include "imodule.h" #include "math/Vector4.h" #include "string/convert.h" @@ -41,6 +42,9 @@ class IGuiExpression // Evaluate this expression to retrieve the result virtual ValueType evaluate() = 0; + + // Value changed signal + virtual sigc::signal& signal_valueChanged() = 0; }; // An expression representing a constant value @@ -51,6 +55,8 @@ class ConstantExpression : private: ValueType _value; + sigc::signal _sigValueChanged; + public: ConstantExpression(const ValueType& value) : _value(value) @@ -61,6 +67,12 @@ class ConstantExpression : return _value; } + // Provide a value changed signal, though it's never invoked + sigc::signal& signal_valueChanged() override + { + return _sigValueChanged; + } + template static std::shared_ptr> Create(const OtherType& value) { @@ -102,6 +114,8 @@ class WindowVariable : // The expression which can be evaluated ExpressionTypePtr _expression; + sigc::connection _exprChangedSignal; + public: typedef std::shared_ptr> Ptr; // smart ptr typedef @@ -119,15 +133,36 @@ class WindowVariable : // to match the ValueType of this variable virtual void setValue(const ExpressionTypePtr& newExpr) { + if (_expression == newExpr) return; + + // Disconnect from any previously subscribed signals + _exprChangedSignal.disconnect(); + _expression = newExpr; + signal_variableChanged().emit(); + + // Subscribe to this new expression's changed signal + if (_expression) + { + _expression->signal_valueChanged().connect([this]() + { + signal_variableChanged().emit(); + }); + } } // Assigns a constant value to this variable virtual void setValue(const ValueType& constantValue) { + // Disconnect from any previously subscribed signals + _exprChangedSignal.disconnect(); + _expression = ConstantExpression::Create(constantValue); + signal_variableChanged().emit(); + + // Since this is a constant expression, we don't need to subscribe to any signal } // Implement the required string->ValueType conversion by means of string::convert<> @@ -269,6 +304,10 @@ class IGui // Sets the given state variable (gui:: = ) virtual void setStateString(const std::string& key, const std::string& value) = 0; + // Retrieve a changed signal for the given key, which is invoked + // whenever setStateString() is called + virtual sigc::signal& getChangedSignalForState(const std::string& key) = 0; + // Returns the state string "gui::" or an empty string if non-existent virtual std::string getStateString(const std::string& key) = 0; diff --git a/plugins/dm.gui/gui/Gui.cpp b/plugins/dm.gui/gui/Gui.cpp index 99754bb79f..43a113fb98 100644 --- a/plugins/dm.gui/gui/Gui.cpp +++ b/plugins/dm.gui/gui/Gui.cpp @@ -45,7 +45,29 @@ void Gui::setStateString(const std::string& key, const std::string& value) { _state[key] = value; - // TODO: Handle state variable links to windowDef registers + // Handle state variable links to windowDef registers + GuiStateChangedSignals::iterator i = _stateSignals.find(key); + + if (i != _stateSignals.end()) + { + i->second.emit(); + } +} + +sigc::signal& Gui::getChangedSignalForState(const std::string& key) +{ + GuiStateChangedSignals::iterator i = _stateSignals.find(key); + + if (i != _stateSignals.end()) + { + return i->second; + } + + // Insert a new signal + std::pair result = + _stateSignals.insert(std::make_pair(key, sigc::signal())); + + return result.first->second; } std::string Gui::getStateString(const std::string& key) diff --git a/plugins/dm.gui/gui/Gui.h b/plugins/dm.gui/gui/Gui.h index 7fafe0adff..5b39419502 100644 --- a/plugins/dm.gui/gui/Gui.h +++ b/plugins/dm.gui/gui/Gui.h @@ -3,6 +3,7 @@ #include "igui.h" #include +#include #include #include "parser/DefTokeniser.h" #include "GuiWindowDef.h" @@ -28,9 +29,12 @@ class Gui : IGuiWindowDefPtr _desktop; // The global GUI state variables - typedef std::map GuiState; + typedef std::unordered_map GuiState; GuiState _state; + typedef std::unordered_map> GuiStateChangedSignals; + GuiStateChangedSignals _stateSignals; + public: const IGuiWindowDefPtr& getDesktop() const override; void setDesktop(const IGuiWindowDefPtr& newDesktop) override; @@ -38,6 +42,9 @@ class Gui : // Sets the given state variable (gui:: = ) void setStateString(const std::string& key, const std::string& value) override; + // Allocate and/or return the changed signal for the given key + sigc::signal& getChangedSignalForState(const std::string& key) override; + // Returns the state string "gui::" or an empty string if non-existent std::string getStateString(const std::string& key) override; diff --git a/plugins/dm.gui/gui/GuiExpression.cpp b/plugins/dm.gui/gui/GuiExpression.cpp index 4ce7463742..b7dc5d3595 100644 --- a/plugins/dm.gui/gui/GuiExpression.cpp +++ b/plugins/dm.gui/gui/GuiExpression.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "itextstream.h" @@ -16,7 +17,16 @@ namespace gui GuiStateVariableExpression::GuiStateVariableExpression(IGui& gui, const std::string& variableName) : _gui(gui), _variableName(variableName) -{} +{ + if (!_variableName.empty()) + { + // Register to the GUI state change signal + _gui.getChangedSignalForState(_variableName).connect([this]() + { + signal_valueChanged().emit(); + }); + } +} float GuiStateVariableExpression::getFloatValue() { @@ -57,6 +67,9 @@ class BinaryExpression : GuiExpressionPtr _b; Precedence _precedence; + sigc::connection _aChanged; + sigc::connection _bChanged; + public: BinaryExpression(Precedence precedence, const GuiExpressionPtr& a = GuiExpressionPtr(), @@ -65,7 +78,23 @@ class BinaryExpression : _a(a), _b(b), _precedence(precedence) - {} + { + if (_a) + { + _aChanged = _a->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } + + if (_b) + { + _bChanged = _b->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } + } Precedence getPrecedence() const { @@ -74,12 +103,34 @@ class BinaryExpression : void setA(const GuiExpressionPtr& a) { + // Disconnect from any previous signal + _aChanged.disconnect(); + _a = a; + + if (_a) + { + _aChanged = _a->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } } void setB(const GuiExpressionPtr& b) { + // Disconnect from any previous signal + _bChanged.disconnect(); + _b = b; + + if (_b) + { + _bChanged = _b->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } } virtual std::string getStringValue() override @@ -707,13 +758,13 @@ class GuiExpressionParser GuiExpression::GuiExpression() {} -GuiExpressionPtr GuiExpression::createFromString(IGui& gui, const std::string& exprStr) +GuiExpressionPtr GuiExpression::CreateFromString(IGui& gui, const std::string& exprStr) { parser::BasicDefTokeniser tokeniser(exprStr, parser::WHITESPACE, "{}(),"); - return createFromTokens(gui, tokeniser); + return CreateFromTokens(gui, tokeniser); } -GuiExpressionPtr GuiExpression::createFromTokens(IGui& gui, parser::DefTokeniser& tokeniser) +GuiExpressionPtr GuiExpression::CreateFromTokens(IGui& gui, parser::DefTokeniser& tokeniser) { // Create an adapter which takes care of splitting the tokens into finer grains // The incoming DefTokeniser is not splitting up expressions like "3*4" without any whitespace in them diff --git a/plugins/dm.gui/gui/GuiExpression.h b/plugins/dm.gui/gui/GuiExpression.h index 12bd2199eb..3e7acf9731 100644 --- a/plugins/dm.gui/gui/GuiExpression.h +++ b/plugins/dm.gui/gui/GuiExpression.h @@ -15,15 +15,24 @@ typedef std::shared_ptr GuiExpressionPtr; // a simple float, a string, a gui variable or a complex formula. class GuiExpression { +protected: + sigc::signal _sigValueChanged; + public: GuiExpression(); virtual float getFloatValue() = 0; virtual std::string getStringValue() = 0; - static GuiExpressionPtr createFromString(IGui& gui, const std::string& exprStr); + // Sub-expressions or clients can subscribe to get notified about changes + sigc::signal& signal_valueChanged() + { + return _sigValueChanged; + } + + static GuiExpressionPtr CreateFromString(IGui& gui, const std::string& exprStr); - static GuiExpressionPtr createFromTokens(IGui& gui, parser::DefTokeniser& tokeniser); + static GuiExpressionPtr CreateFromTokens(IGui& gui, parser::DefTokeniser& tokeniser); }; // An expression representing a constant floating point value @@ -77,10 +86,22 @@ class TypedExpression : private: GuiExpressionPtr _contained; + sigc::signal _sigValueChanged; + public: TypedExpression(const GuiExpressionPtr& contained) : _contained(contained) - {} + { + if (_contained) + { + // Connect the changed signal of the contained expression + // to fire our own signal + _contained->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } + } // The generic evaluate() implementation will try to cast the string // value of the GuiExpression to the desired ValueType @@ -88,6 +109,11 @@ class TypedExpression : { return string::convert(_contained->getStringValue()); } + + sigc::signal& signal_valueChanged() override + { + return _sigValueChanged; + } }; // Boolean specialisation, making use of the contained getFloatValue() @@ -99,15 +125,32 @@ class TypedExpression : private: GuiExpressionPtr _contained; + sigc::signal _sigValueChanged; + public: TypedExpression(const GuiExpressionPtr& contained) : _contained(contained) - {} + { + if (_contained) + { + // Connect the changed signal of the contained expression + // to fire our own signal + _contained->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } + } virtual bool evaluate() override { return _contained->getFloatValue() != 0.0f; } + + sigc::signal& signal_valueChanged() override + { + return _sigValueChanged; + } }; // An expression representing a Vector4 value @@ -118,6 +161,8 @@ class Vector4Expression : private: std::vector _vec; + sigc::signal _sigValueChanged; + public: Vector4Expression(const GuiExpressionPtr& x, const GuiExpressionPtr& y, const GuiExpressionPtr& z, const GuiExpressionPtr& w) : @@ -127,6 +172,16 @@ class Vector4Expression : _vec[1] = y; _vec[2] = z; _vec[3] = w; + + for (const GuiExpressionPtr& comp : _vec) + { + if (!comp) continue; + + comp->signal_valueChanged().connect([this]() + { + signal_valueChanged().emit(); + }); + } } virtual Vector4 evaluate() override @@ -134,6 +189,11 @@ class Vector4Expression : return Vector4(_vec[0]->getFloatValue(), _vec[1]->getFloatValue(), _vec[2]->getFloatValue(), _vec[3]->getFloatValue()); } + + sigc::signal& signal_valueChanged() override + { + return _sigValueChanged; + } }; class GuiStateVariableExpression : diff --git a/plugins/dm.gui/gui/GuiScript.cpp b/plugins/dm.gui/gui/GuiScript.cpp index 7ec75b5fa2..ab6a58bdfe 100644 --- a/plugins/dm.gui/gui/GuiScript.cpp +++ b/plugins/dm.gui/gui/GuiScript.cpp @@ -327,7 +327,7 @@ std::size_t GuiScript::pushStatement(const StatementPtr& statement) GuiExpressionPtr GuiScript::getExpression(parser::DefTokeniser& tokeniser) { - return GuiExpression::createFromTokens(_owner.getGui(), tokeniser); + return GuiExpression::CreateFromTokens(_owner.getGui(), tokeniser); } GuiExpressionPtr GuiScript::getIfExpression(parser::DefTokeniser& tokeniser) diff --git a/plugins/dm.gui/gui/GuiWindowDef.cpp b/plugins/dm.gui/gui/GuiWindowDef.cpp index 59974b99e7..39183034cd 100644 --- a/plugins/dm.gui/gui/GuiWindowDef.cpp +++ b/plugins/dm.gui/gui/GuiWindowDef.cpp @@ -77,7 +77,7 @@ IGui& GuiWindowDef::getGui() const // Returns a GUI expression, which can be a number, a string or a formula ("gui::objVisible" == 1). GuiExpressionPtr GuiWindowDef::getExpression(parser::DefTokeniser& tokeniser) { - return GuiExpression::createFromTokens(_owner, tokeniser); + return GuiExpression::CreateFromTokens(_owner, tokeniser); } std::shared_ptr> GuiWindowDef::parseVector4(parser::DefTokeniser& tokeniser)