Răsfoiți Sursa

Add unsubscribe functionality to event implementation

Patrick-Christopher Mattulat 6 luni în urmă
părinte
comite
80e212d078

+ 2 - 0
include/ls-std/event/EventListener.hpp

@@ -27,6 +27,7 @@ namespace ls::std::event
       [[nodiscard]] ls::std::event::type::listener_id getId() const;
       void setId(ls::std::event::type::listener_id _id);
       [[maybe_unused]] bool subscribe(const ls::std::event::Event &_event, const ls::std::event::type::event_action &_action);
+      [[maybe_unused]] bool unsubscribe(const ls::std::event::Event &_event);
 
     private:
 
@@ -34,6 +35,7 @@ namespace ls::std::event
 
       void _requestListenerId(const ::std::shared_ptr<ls::std::core::Class> &_manager);
       [[nodiscard]] bool _subscribe(const ls::std::event::Event &_event, const ls::std::event::type::event_action &_action);
+      [[nodiscard]] bool _unsubscribe(const ls::std::event::Event &_event);
   };
 }
 

+ 1 - 0
include/ls-std/event/EventManager.hpp

@@ -39,6 +39,7 @@ namespace ls::std::event
       void invoke(const ls::std::event::Event &_event) const;
       [[nodiscard]] ls::std::event::type::listener_id requestListenerId();
       void subscribeListenerForEvent(::std::shared_ptr<ls::std::event::EventListener> _listener, const ls::std::event::Event &_event, ls::std::event::type::event_action _action);
+      void unsubscribeListenerForEvent(const ::std::shared_ptr<ls::std::event::EventListener> &_listener, const ls::std::event::Event &_event);
 
     private:
 

+ 19 - 0
source/ls-std/event/EventListener.cpp

@@ -44,6 +44,12 @@ bool EventListener::subscribe(const Event &_event, const event_action &_action)
   return this->_subscribe(_event, _action);
 }
 
+bool EventListener::unsubscribe(const Event &_event)
+{
+  NullPointerEvaluator(_event.getManager(), "no event manager is provided for " + _event.getClassName()).evaluate();
+  return this->_unsubscribe(_event);
+}
+
 void EventListener::_requestListenerId(const shared_ptr<Class> &_manager)
 {
   shared_ptr<EventManager> manager = dynamic_pointer_cast<EventManager>(_manager);
@@ -66,3 +72,16 @@ bool EventListener::_subscribe(const Event &_event, const event_action &_action)
 
   return subscribed;
 }
+
+bool EventListener::_unsubscribe(const Event &_event)
+{
+  bool unsubscribed{};
+
+  if (shared_ptr<EventManager> manager = dynamic_pointer_cast<EventManager>(_event.getManager()); manager->holdsListenerForEvent(this->id, _event))
+  {
+    manager->unsubscribeListenerForEvent(shared_from_this(), _event);
+    unsubscribed = !manager->holdsListenerForEvent(this->id, _event);
+  }
+
+  return unsubscribed;
+}

+ 15 - 0
source/ls-std/event/EventManager.cpp

@@ -88,6 +88,21 @@ void EventManager::subscribeListenerForEvent(shared_ptr<EventListener> _listener
   this->inventory[_event.getClassName()].push_back(inventoryEntry);
 }
 
+void EventManager::unsubscribeListenerForEvent(const shared_ptr<EventListener> &_listener, const Event &_event)
+{
+  if (this->_observesEvent(_event))
+  {
+    for (auto iterator = this->inventory[_event.getClassName()].begin(); iterator != this->inventory[_event.getClassName()].end(); iterator++)
+    {
+      if (iterator->first->getId() == _listener->getId())
+      {
+        this->inventory[_event.getClassName()].erase(iterator);
+        break;
+      }
+    }
+  }
+}
+
 void EventManager::_notifyListeners(const event_listeners &_listeners)
 {
   for (const auto &[listener, eventAction] : _listeners)

+ 41 - 0
test/cases/event/EventListenerTest.cpp

@@ -87,4 +87,45 @@ namespace
     ASSERT_TRUE(myButton->subscribe(OnClickEvent().of(eventManager), [myButton]() mutable { myButton->onClickEvent(); }));
     ASSERT_FALSE(myButton->subscribe(OnClickEvent().of(eventManager), [myButton]() mutable { myButton->onClickEvent(); }));
   }
+
+  TEST_F(EventListenerTest, unsubscribe_with_missing_event_manager)
+  {
+    auto myButton = make_shared<Button>();
+
+    EXPECT_THROW(
+        {
+          try
+          {
+            myButton->unsubscribe(OnClickEvent());
+          }
+          catch (const NullPointerException &_exception)
+          {
+            string actual = _exception.what();
+            string expected = _exception.getName() + " thrown - no event manager is provided for OnClickEvent";
+
+            EXPECT_STREQ(expected.c_str(), actual.c_str());
+            throw;
+          }
+        },
+        NullPointerException);
+  }
+
+  TEST_F(EventListenerTest, unsubscribe)
+  {
+    auto myButton = make_shared<Button>();
+    auto eventManager = make_shared<EventManager>();
+
+    ASSERT_TRUE(myButton->subscribe(OnClickEvent().of(eventManager), [myButton]() mutable { myButton->onClickEvent(); }));
+    ASSERT_TRUE(myButton->unsubscribe(OnClickEvent().of(eventManager)));
+  }
+
+  TEST_F(EventListenerTest, unsubscribe_second_time)
+  {
+    auto myButton = make_shared<Button>();
+    auto eventManager = make_shared<EventManager>();
+
+    ASSERT_TRUE(myButton->subscribe(OnClickEvent().of(eventManager), [myButton]() mutable { myButton->onClickEvent(); }));
+    ASSERT_TRUE(myButton->unsubscribe(OnClickEvent().of(eventManager)));
+    ASSERT_FALSE(myButton->unsubscribe(OnClickEvent().of(eventManager)));
+  }
 }

+ 11 - 0
test/cases/event/EventManagerTest.cpp

@@ -101,4 +101,15 @@ namespace
     eventManager->subscribeListenerForEvent(myButton, OnClickEvent(), [myButton]() mutable { myButton->onClickEvent(); });
     ASSERT_TRUE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
   }
+
+  TEST_F(EventManagerTest, unsubscribeListenerForEvent)
+  {
+    auto eventManager = make_shared<EventManager>();
+    auto myButton = make_shared<Button>();
+
+    eventManager->subscribeListenerForEvent(myButton, OnClickEvent(), [myButton]() mutable { myButton->onClickEvent(); });
+    ASSERT_TRUE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
+    eventManager->unsubscribeListenerForEvent(myButton, OnClickEvent());
+    ASSERT_FALSE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
+  }
 }