Browse Source

signal on replace

Michele Caini 7 years ago
parent
commit
4292d2ce86
3 changed files with 51 additions and 23 deletions
  1. 12 5
      docs/md/entity.md
  2. 35 13
      src/entt/entity/registry.hpp
  3. 4 5
      test/entt/entity/registry.cpp

+ 12 - 5
docs/md/entity.md

@@ -348,7 +348,8 @@ registry.construction<position>().disconnect<&my_class::member>(&instance);
 ```
 
 To be notified when components are destroyed, use the `destruction` member
-function instead.
+function instead. Finally, the `update` member function will return a sink to
+which to connect listeners to observe changes on components.
 
 The function type of a listener for the construction signal should be equivalent
 to the following:
@@ -361,8 +362,9 @@ Where `Component` is intuitively the type of component of interest. In other
 words, a listener is provided with the registry that triggered the notification
 and the entity affected by the change, in addition to the newly created
 instance.<br/>
-The function type of a listener for the destruction signal is the same, except
-for the `Component` parameter:
+The function type of a listener to be notified about changes is the same of that
+of the construction signal. The one the destruction signal is also similar,
+except for the `Component` parameter:
 
 ```cpp
 void(registry &, entt::entity);
@@ -375,8 +377,13 @@ this case, the registry is made available for the purpose.
 
 Note also that:
 
-* Listeners are invoked **after** components have been assigned to entities.
-* Listeners are invoked **before** components have been removed from entities.
+* Listeners for the construction signal are invoked **after** components have
+  been assigned to entities.
+* Listeners designed to observe changes are invoked **before** components have
+  been replaced and therefore before newly created instances have been assigned
+  to entities.
+* Listeners for the destruction signal are invoked **before** components have
+  been removed from entities.
 * The order of invocation of the listeners isn't guaranteed in any case.
 
 There are also some limitations on what a listener can and cannot do. In

+ 35 - 13
src/entt/entity/registry.hpp

@@ -91,14 +91,15 @@ class basic_registry {
 
         template<typename... Args>
         Component & replace(const Entity entt, Args &&... args) {
-            destruction.publish(*owner, entt);
-            auto &component = sparse_set<Entity, Component>::get(entt);
-            component = Component{std::forward<Args>(args)...};
-            construction.publish(*owner, entt, component);
-            return component;
+            Component component{std::forward<Args>(args)...};
+            update.publish(*owner, entt, component);
+            auto &other = sparse_set<Entity, Component>::get(entt);
+            std::swap(other, component);
+            return other;
         }
 
         sigh<void(basic_registry &, const Entity, Component &)> construction;
+        sigh<void(basic_registry &, const Entity, Component &)> update;
         sigh<void(basic_registry &, const Entity)> destruction;
         basic_registry *owner;
     };
@@ -394,12 +395,6 @@ public:
     using size_type = typename sparse_set<Entity>::size_type;
     /*! @brief Unsigned integer type. */
     using component_type = ENTT_ID_TYPE;
-    /*! @brief Destruction sink type. */
-    using destruction_sink_type = typename sigh<void(basic_registry &, const Entity)>::sink_type;
-
-    /*! @brief Construction sink type for the given component. */
-    template<typename Component>
-    using construction_sink_type = typename sigh<void(basic_registry &, const Entity, Component &)>::sink_type;
 
     /*! @brief Default constructor. */
     basic_registry() ENTT_NOEXCEPT = default;
@@ -1052,10 +1047,37 @@ public:
      * @return A temporary sink object.
      */
     template<typename Component>
-    construction_sink_type<Component> construction() ENTT_NOEXCEPT {
+    auto construction() ENTT_NOEXCEPT {
         return assure<Component>()->construction.sink();
     }
 
+    /**
+     * @brief Returns a sink object for the given component.
+     *
+     * A sink is an opaque object used to connect listeners to components.<br/>
+     * The sink returned by this function can be used to receive notifications
+     * whenever an instance of the given component is updated.
+     *
+     * The function type for a listener is equivalent to:
+     * @code{.cpp}
+     * void(registry<Entity> &, Entity, Component &);
+     * @endcode
+     *
+     * Listeners are invoked **before** the component has been replaced. The
+     * order of invocation of the listeners isn't guaranteed.<br/>
+     * Note also that the greater the number of listeners, the greater the
+     * performance hit when a new component is created.
+     *
+     * @sa sink
+     *
+     * @tparam Component Type of component of which to get the sink.
+     * @return A temporary sink object.
+     */
+    template<typename Component>
+    auto update() ENTT_NOEXCEPT {
+        return assure<Component>()->update.sink();
+    }
+
     /**
      * @brief Returns a sink object for the given component.
      *
@@ -1080,7 +1102,7 @@ public:
      * @return A temporary sink object.
      */
     template<typename Component>
-    destruction_sink_type destruction() ENTT_NOEXCEPT {
+    auto destruction() ENTT_NOEXCEPT {
         return assure<Component>()->destruction.sink();
     }
 

+ 4 - 5
test/entt/entity/registry.cpp

@@ -912,19 +912,18 @@ TEST(Registry, Signals) {
     registry.destruction<int>().disconnect<&listener::decr<int>>(&listener);
     registry.assign_or_replace<int>(e0);
 
-    ASSERT_EQ(listener.counter, 2);
+    ASSERT_EQ(listener.counter, 1);
     ASSERT_EQ(listener.last, e0);
 
-    registry.construction<int>().disconnect<&listener::incr<int>>(&listener);
-    registry.destruction<int>().connect<&listener::decr<int>>(&listener);
+    registry.update<int>().connect<&listener::incr<int>>(&listener);
     registry.assign_or_replace<int>(e0);
 
-    ASSERT_EQ(listener.counter, 1);
+    ASSERT_EQ(listener.counter, 2);
     ASSERT_EQ(listener.last, e0);
 
     registry.replace<int>(e0);
 
-    ASSERT_EQ(listener.counter, 0);
+    ASSERT_EQ(listener.counter, 3);
     ASSERT_EQ(listener.last, e0);
 }