From 314c6c0848a48f2b455164fdf2f8b6bdff910697 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 11 Jul 2017 14:23:17 -0400 Subject: [PATCH] Enable Property to have multiple callbacks --- include/openspace/properties/property.h | 34 +++++++++++++++++---- src/properties/property.cpp | 39 +++++++++++++++++++++---- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/include/openspace/properties/property.h b/include/openspace/properties/property.h index 5fbc8e6a78..80b446b5a3 100644 --- a/include/openspace/properties/property.h +++ b/include/openspace/properties/property.h @@ -73,6 +73,14 @@ public: User = 0 ///< Visible in User mode }; + /// An OnChangeHandle is returned by the onChange method to uniquely identify an + /// onChange callback + using OnChangeHandle = uint32_t; + + /// This OnChangeHandle can be used to remove all onChange callbacks from this + /// Property + static OnChangeHandle OnChangeHandleAll; + /** * The constructor for the property. The identifier needs to be unique * for each PropertyOwner. The guiName will be stored in the metaData @@ -92,7 +100,7 @@ public: * The destructor taking care of deallocating all unused memory. This method will not * remove the Property from the PropertyOwner. */ - virtual ~Property(); + virtual ~Property() = default; /** * This method returns the class name of the Property. The method is used by the @@ -191,12 +199,25 @@ public: * This method registers a callback function that will be called every * time if either Property:set or Property::setLuaValue was called with a value that * is different from the previously stored value. The callback can be removed by - * passing an empty std::function object. + * calling the removeOnChange method with the OnChangeHandle that was returned here. * \param callback The callback function that is called when the encapsulated type has * been successfully changed by either the Property::set or Property::setLuaValue * methods. + * \pre The callback must not be empty + * \return An OnChangeHandle that can be used in subsequent calls to remove a callback */ - virtual void onChange(std::function callback); + OnChangeHandle onChange(std::function callback); + + /** + * This method deregisters a callback that was previously registered with the onChange + * method. If OnChangeHandleAll is passed to this function, all registered callbacks + * are removed. + * \param handle An OnChangeHandle that was returned from a previous call to onChange + * by this property or OnChangeHandleAll if all callbacks should be removed. + * \pre handle must refer to a callback that has been previously registred + * \pre handle must refer to a callback that has not been removed previously + */ + void removeOnChange(OnChangeHandle handle); /** * This method returns the unique identifier of this Property. @@ -389,8 +410,11 @@ protected: /// The Dictionary containing all meta data necessary for external applications ghoul::Dictionary _metaData; - /// The callback function that will be invoked whenever the encapsulated value changes - std::function _onChangeCallback; + /// The callback function sthat will be invoked whenever the value changes + std::vector>> _onChangeCallbacks; + +private: + OnChangeHandle _currentHandleValue; }; } // namespace properties diff --git a/src/properties/property.cpp b/src/properties/property.cpp index 0c5cd0d967..237fed2092 100644 --- a/src/properties/property.cpp +++ b/src/properties/property.cpp @@ -40,6 +40,9 @@ namespace { const char* _metaDataKeyViewPrefix = "view."; } +Property::OnChangeHandle Property::OnChangeHandleAll = + std::numeric_limits::max(); + const char* Property::ViewOptions::Color = "color"; const char* Property::ViewOptions::LightPosition = "lightPosition"; @@ -51,6 +54,7 @@ const char* Property::MetaDataKey = "MetaData"; Property::Property(std::string identifier, std::string guiName, Visibility visibility) : _owner(nullptr) , _identifier(std::move(identifier)) + , _currentHandleValue(0) { ghoul_assert(!_identifier.empty(), "Identifier must not be empty"); ghoul_assert(!guiName.empty(), "guiName must not be empty"); @@ -59,8 +63,6 @@ Property::Property(std::string identifier, std::string guiName, Visibility visib _metaData.setValue(MetaDataKeyGuiName, std::move(guiName)); } -Property::~Property() {} - const std::string& Property::identifier() const { return _identifier; } @@ -163,8 +165,33 @@ const ghoul::Dictionary& Property::metaData() const { return _metaData; } -void Property::onChange(std::function callback) { - _onChangeCallback = std::move(callback); +Property::OnChangeHandle Property::onChange(std::function callback) { + ghoul_assert(callback, "The callback must not be empty"); + OnChangeHandle handle = _currentHandleValue++; + _onChangeCallbacks.emplace_back(handle, std::move(callback)); + return handle; +} + +void Property::removeOnChange(OnChangeHandle handle) { + if (handle == OnChangeHandleAll) { + _onChangeCallbacks.clear(); + } + else { + auto it = std::find_if( + _onChangeCallbacks.begin(), + _onChangeCallbacks.end(), + [handle](const std::pair>& p) { + return p.first == handle; + } + ); + + ghoul_assert( + it != _onChangeCallbacks.end(), + "handle must be a valid callback handle" + ); + + _onChangeCallbacks.erase(it); + } } PropertyOwner* Property::owner() const { @@ -176,8 +203,8 @@ void Property::setPropertyOwner(PropertyOwner* owner) { } void Property::notifyListener() { - if (_onChangeCallback) { - _onChangeCallback(); + for (const std::pair>& p : _onChangeCallbacks) { + p.second(); } }