diff --git a/include/openspace/properties/property.h b/include/openspace/properties/property.h index aed1f96afb..93ed912bbd 100644 --- a/include/openspace/properties/property.h +++ b/include/openspace/properties/property.h @@ -39,42 +39,216 @@ namespace properties { class PropertyOwner; +/** + * A property encapsulates a value which should be user-changeable. A property almost + * always belongs to a PropertyOwner who has taken ownership (setPropertyOwner) of the + * Property. Per PropertyOwner, the identifier needs to be unique and can be + * used as a URI. This class is an abstract base class and each subclass (most notable + * TemplateProperty) needs to implement the methods Property::className, Property::get, + * Property::set, Property::type(), Property::getLua, Property::setLua, and + * Property::typeLua to make full use of the infrastructure. + * The most common types can be implemented by creating a specialized instantiation of + * TemplateProperty, which provides default implementations for these methods. + * + * The onChange method can be used by the PropertyOwner to listen to changes that happen + * to the Property. The parameter is a function object that gets called after new value + * has been set. + * The metaData allows the developer to specify additional information about the Property + * which might be used in GUI representations. One example would be a glm::vec4 property, + * (Vec4Property) that can either represent a 4-dimensional position, a powerscaled + * coordinate, a light position, or other things, requiring different GUI representations. + * \see TemplateProperty + * \see PropertyOwner + */ class Property { public: + /** + * The constructor for the property. The identifier needs to be unique + * for each PropertyOwner. The guiName will be stored in the metaData + * to be accessed by the GUI elements using the guiName key. The default + * visibility settings is true, whereas the default read-only state is + * false. + * \param identifier A unique identifier for this property. It has to be unique to the + * PropertyOwner + * \param guiName The human-readable GUI name for this Property + */ Property(std::string identifier, std::string guiName); + + /** + * The destructor taking care of deallocating all unused memory. This method will not + * remove the Property from the PropertyOwner. + */ virtual ~Property(); - //virtual Property* create() const = 0; + /** + * This method returns the class name of the Property. The method is used by the + * TemplateFactory to create new instances of Propertys. The returned value is almost + * always identical to the C++ class name of the derived class. + * \return The class name of the Property + */ virtual std::string className() const = 0; + /** + * This method returns the encapsulated value of the Property to the caller. The type + * that is returned is determined by the type function and is up to the developer of + * the derived class. The default implementation returns an empty boost::any object. + * \return The value that is encapsulated by this Property, or an empty boost::any + * object if the method was not overritten. + */ virtual boost::any get() const; - virtual void set(boost::any value); - virtual const std::type_info& type() const; + /** + * Sets the value encapsulated by this Property to the value passed to + * this function. It is the caller's responsibility to ensure that the type contained + * in value is compatible with the concrete subclass of the Property. The + * method Property::type will return the desired type for the Property. The default + * implementation of this method ignores the input. + * \param value The new value that should be stored in this Property + */ + virtual void set(boost::any value); + + /** + * This method returns the type that is requested by this Property for the set method. + * The default implementation returns the type of void. + * \return The type that is requested by this Property's Property::set method + */ + virtual const std::type_info& type() const; + + /** + * This method encodes the encapsulated value of this Property at the top of the Lua + * stack. The specific details of this serialization is up to the property developer + * as long as the rest of the stack is unchanged. The implementation has to be + * synchronized with the Property::setLua method. The default implementation is a + * no-op. + * \param state The Lua state to which the value will be encoded + * \return true if the encoding succeeded, false otherwise + */ virtual bool getLua(lua_State* state) const; + + /** + * This method sets the value encapsulated by this Property by deserializing the value + * on top of the passed Lua stack. The specific details of the deserialization are up + * to the Property developer, but they must only depend on the top element of the + * stack and must leave all other elements unchanged. The implementation has to be + * synchronized with the Property::getLua method. The default implementation is a + * no-op. + * \param state The Lua state from which the value will be decoded + * \return true if the decoding and setting of the value succeeded, + * false otherwise + */ virtual bool setLua(lua_State* state); + + /** + * Returns the Lua type that will be put onto the stack in the Property::getLua method + * and which will be consumed by the Property::setLua method. The returned value + * can belong to the set of Lua types: LUA_TNONE, LUA_TNIL, + * LUA_TBOOLEAN, LUA_TLIGHTUSERDATA, + * LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, + * LUA_TFUNCTION, LUA_TUSERDATA, or + * LUA_TTHREAD. The default implementation will return + * LUA_TNONE. + * \return The Lua type that will be consumed or produced by the Property::getLua and + * Property::setLua methods. + */ virtual int typeLua() const; + /** + * This method registers a callback function that will be called every + * time if either Property:set or Property::setLua was called with a value that is + * different from the previously stored value. The callback can be removed my passing + * an empty std::function object. + * \param callback The callback function that is called when the encapsulated type has + * been successfully changed by either the Property::set or Property::setLua methods. + */ virtual void onChange(std::function callback); + /** + * This method returns the unique identifier of this Property. + * \return The unique identifier of this Property + */ const std::string& identifier() const; - const std::string& guiName() const; + /** + * Returns the PropertyOwner of this Property or nullptr, if it does not + * have an owner. + *\ return The Property of this Property + */ PropertyOwner* owner() const; - void setPropertyOwner(PropertyOwner* owner); + /** + * Assigned the Property to a new PropertyOwner. This method does not inform the + * PropertyOwner of this action. + * \param owner The new PropertyOwner for this Property + */ + void setPropertyOwner(PropertyOwner* owner); + + /** + * Returns the human-readable GUI name for this Property that has been set in the + * constructor. This method returns the same value as accessing the metaData object + * and requesting the std::string stored for the guiName + * key. + * \return The human-readable GUI name for this Property + */ + const std::string& guiName() const; + + /** + * Sets the identifier of the group that this Property belongs to. Property groups can + * be used, for example, by GUI application to visually group different properties, + * but it has no impact on the Property itself. The default value for the groupID is + * "". + * \param groupId The group id that this property should belong to + */ void setGroupIdentifier(std::string groupId); + + /** + * Returns the group idenfier that this Property belongs to, or "" if it + * belongs to no group. + * \return The group identifier that this Property belongs to + */ std::string groupIdentifier() const; + /** + * Determines a hint if this Property should be visible, or hidden. Each application + * accessing the properties is free to ignore this hint. It is stored in the metaData + * Dictionary with the key: isVisible. The default value is + * true. + * \param state true if the Property should be visible, + * false otherwise. + */ void setVisible(bool state); - bool isVisible() const; - void setReadOnly(bool state); - bool isReadOnly() const; + /** + * This method determines if this Property should be read-only in external + * applications. This setting is only a hint and does not need to be followed by GUI + * applications and does not have any effect on the Property::set or Property::setLua + * methods. The value is stored in the metaData Dictionary with the key: + * isReadOnly. The default value is false. + * \param state true if the Property should be read only, + * false otherwise + */ + void setReadOnly(bool state); + /** + * This method allows the developer to give hints to the GUI about different + * representations for the GUI. The same Property (for example Vec4Property) can be + * used in different ways, each requiring a different input method. These values are + * stored in the metaData object using the views. prefix in front of the + * option parameter. See Property::ViewOptions for a default list of + * possible options. As these are only hints, the GUI is free to ignore any suggestion + * by the developer. + * \param option The view option that should be modified + * \param value Determines if the view option should be active (true) or + * deactivated (false) + */ void setViewOption(std::string option, bool value = true); - bool viewOption(const std::string& option) const; + /** + * Default view options that can be used in the Property::setViewOption method. The + * values are: Property::ViewOptions::Color = color, + * Property::ViewOptions::LightPosition = lightPosition, + * Property::ViewOptions::PowerScaledScalar = powerScaledScalar, and + * Property::ViewOptions::PowerScaledCoordinate = powerScaledCoordinate. + */ struct ViewOptions { static const std::string Color; static const std::string LightPosition; @@ -82,18 +256,32 @@ public: static const std::string PowerScaledCoordinate; }; + /** + * Returns the metaData that contains all information for external applications to + * correctly display information about the Property. No information that is stored in + * this Dictionary is necessary for the programmatic use of the Property. + * \return The Dictionary containing all meta data information about this Property + */ const ghoul::Dictionary& metaData() const; protected: - void notifyListeners(); + /** + * This method must be called by all subclasses whenever the encapsulated value has + * changed and a potential listener has to be informed. + */ + void notifyListener(); - PropertyOwner* _owner; + /// The PropetyOwner this Property belongs to, or nullptr + PropertyOwner* _owner; + /// The identifier for this Property std::string _identifier; - std::string _guiName; + + /// The Dictionary containing all meta data necessary for external applications ghoul::Dictionary _metaData; - std::vector> _onChangeCallbacks; + /// The callback function that will be invoked whenever the encapsulated value changes + std::function _onChangeCallback; }; } // namespace properties diff --git a/include/openspace/properties/templateproperty.inl b/include/openspace/properties/templateproperty.inl index 2db1902329..ae81fd4836 100644 --- a/include/openspace/properties/templateproperty.inl +++ b/include/openspace/properties/templateproperty.inl @@ -133,7 +133,7 @@ template void TemplateProperty::set(boost::any value) { try { _value = boost::any_cast(std::move(value)); - notifyListeners(); + notifyListener(); } catch (boost::bad_any_cast&) { LERRORC("TemplateProperty", "Illegal cast from '" << value.type().name() diff --git a/src/properties/property.cpp b/src/properties/property.cpp index 7ea90bcbe7..f00890f9bd 100644 --- a/src/properties/property.cpp +++ b/src/properties/property.cpp @@ -30,9 +30,12 @@ namespace openspace { namespace properties { namespace { + const std::string _metaDataKeyGuiName = "guiName"; const std::string _metaDataKeyGroup = "group"; const std::string _metaDataKeyVisible = "isVisible"; const std::string _metaDataKeyReadOnly = "isReadOnly"; + + const std::string _metaDataKeyViewPrefix = "view."; } const std::string Property::ViewOptions::Color = "color"; @@ -43,9 +46,9 @@ const std::string Property::ViewOptions::PowerScaledScalar = "powerScaledScalar" Property::Property(std::string identifier, std::string guiName) : _identifier(std::move(identifier)) - , _guiName(std::move(guiName)) { setVisible(true); + _metaData.setValue(_metaDataKeyGuiName, std::move(guiName)); } Property::~Property() {} @@ -77,7 +80,9 @@ int Property::typeLua() const { } const std::string& Property::guiName() const { - return _guiName; + std::string result = ""; + _metaData.getValue(_metaDataKeyGuiName, result); + return std::move(result); } void Property::setGroupIdentifier(std::string groupId) { @@ -94,30 +99,12 @@ void Property::setVisible(bool state) { _metaData.setValue(_metaDataKeyVisible, state); } -bool Property::isVisible() const { - bool result = false; - _metaData.getValue(_metaDataKeyVisible, result); - return result; -} - void Property::setReadOnly(bool state) { _metaData.setValue(_metaDataKeyReadOnly, state); } -bool Property::isReadOnly() const { - bool result = false; - _metaData.getValue(_metaDataKeyReadOnly, result); - return result; -} - void Property::setViewOption(std::string option, bool value) { - _metaData.setValue("view." + option, value, true); -} - -bool Property::viewOption(const std::string& option) const { - bool result = false; - _metaData.getValue("view." + option, result); - return result; + _metaData.setValue(_metaDataKeyViewPrefix + option, value, true); } const ghoul::Dictionary& Property::metaData() const { @@ -125,7 +112,7 @@ const ghoul::Dictionary& Property::metaData() const { } void Property::onChange(std::function callback) { - _onChangeCallbacks.push_back(std::move(callback)); + _onChangeCallback = std::move(callback); } @@ -139,9 +126,8 @@ void Property::setPropertyOwner(PropertyOwner* owner) _owner = owner; } -void Property::notifyListeners() { - for (std::function& f : _onChangeCallbacks) - f(); +void Property::notifyListener() { + _onChangeCallback(); } } // namespace properties