1#pragma once
2
3// DelegateEvent is used to implement a Qt-style signal/slot mechanism.
4
5#include <container/seadTList.h>
6#include <heap/seadDisposer.h>
7#include <prim/seadDelegate.h>
8#include <prim/seadStorageFor.h>
9
10namespace sead
11{
12/// Manages signal and slots for an event.
13template <typename T>
14class DelegateEvent
15{
16public:
17 class Slot;
18 using SlotList = TList<Slot*>;
19 using SlotListNode = TListNode<Slot*>;
20
21 /// A Slot is a wrapper around a Delegate that is invoked when a signal is emitted.
22 class Slot : public IDisposer
23 {
24 public:
25 template <typename TDelegate>
26 Slot(TDelegate delegate) // NOLINT(google-explicit-constructor)
27 {
28 mDelegate.construct(std::move(delegate));
29 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
30 mDelegatePtr = mDelegate->getDelegate();
31 }
32
33 template <typename C>
34 Slot(C* instance, void (C::*func)(T)) : Slot(Delegate1<C, T>(instance, func))
35 {
36 }
37
38 ~Slot() override { release(); }
39
40 void release()
41 {
42 if (mConnectedToDelegateEvent)
43 {
44 mNode.erase();
45 mConnectedToDelegateEvent = false;
46 }
47 }
48
49 private:
50 friend class DelegateEvent;
51
52 void invoke_(T arg)
53 {
54 if (mDelegatePtr)
55 (*mDelegatePtr)(arg);
56 }
57
58 SlotListNode mNode{this};
59 IDelegate1<T>* mDelegatePtr = nullptr;
60 StorageFor<AnyDelegate1<T>, true> mDelegate;
61 bool mConnectedToDelegateEvent = false;
62 };
63
64 virtual ~DelegateEvent()
65 {
66 auto it = mList.begin();
67 while (it != mList.end())
68 {
69 Slot* ptr = *it;
70 ++it;
71 ptr->release();
72 }
73 }
74
75 void connect(Slot& slot)
76 {
77 slot.release();
78 mList.pushBack(&slot.mNode);
79 slot.mConnectedToDelegateEvent = true;
80 }
81
82 void disconnect(Slot& slot) { slot.release(); }
83
84 void emit(T arg)
85 {
86 for (auto& slot_node : mList.robustRange())
87 slot_node.mData->invoke_(arg);
88 }
89
90 int getNumSlots() const { return mList.size(); }
91
92protected:
93 SlotList mList;
94};
95
96} // namespace sead
97