178 lines
4.2 KiB
C++
178 lines
4.2 KiB
C++
#pragma once
|
|
|
|
#include <types/base/types.h>
|
|
#include <functional>
|
|
#include <tuple>
|
|
#include <vector>
|
|
#include <cstddef>
|
|
|
|
namespace extra2d::event {
|
|
|
|
/**
|
|
* @brief 监听器链表节点
|
|
*/
|
|
struct ListenerEntry {
|
|
ListenerEntry* next = nullptr;
|
|
ListenerEntry* prev = nullptr;
|
|
class ListenerBase* listener = nullptr;
|
|
};
|
|
|
|
/**
|
|
* @brief 监听器基类
|
|
*/
|
|
class ListenerBase {
|
|
protected:
|
|
ListenerEntry* entry_ = nullptr;
|
|
friend class ListenerContainer;
|
|
};
|
|
|
|
/**
|
|
* @brief 监听器容器,管理同一事件的所有监听器
|
|
*/
|
|
class ListenerContainer {
|
|
public:
|
|
ListenerContainer() = default;
|
|
~ListenerContainer();
|
|
|
|
template<typename EHandler, typename... Args>
|
|
bool broadcast(Args&&... args);
|
|
|
|
protected:
|
|
ListenerEntry* listenerList_ = nullptr;
|
|
ListenerEntry* listenersToAdd_ = nullptr;
|
|
std::vector<ListenerEntry*> listenersToRemove_;
|
|
int broadcasting_ = 0;
|
|
|
|
void addListener(ListenerBase* listener);
|
|
void removeListener(ListenerBase* listener);
|
|
void processPendingListeners();
|
|
bool hasPendingListeners() const;
|
|
|
|
template<typename EHandler>
|
|
friend class Listener;
|
|
};
|
|
|
|
/**
|
|
* @brief 事件处理器数据库,每个事件类型一个容器
|
|
*/
|
|
template<typename EHandler>
|
|
class ListenerDB {
|
|
public:
|
|
static ListenerContainer* container() {
|
|
static ListenerContainer* ctn = new ListenerContainer();
|
|
return ctn;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief 事件特征定义
|
|
*/
|
|
template<typename BusT, typename... Args>
|
|
struct EventTrait {
|
|
using Bus = BusT;
|
|
using ArgTuple = std::tuple<Args...>;
|
|
static constexpr size_t ARG_COUNT = sizeof...(Args);
|
|
};
|
|
|
|
/**
|
|
* @brief 元组提取器
|
|
*/
|
|
template<typename T>
|
|
struct TupleExtractor {
|
|
using FuncType = void();
|
|
using StdFuncType = std::function<void()>;
|
|
};
|
|
|
|
template<typename... Args>
|
|
struct TupleExtractor<std::tuple<Args...>> {
|
|
using FuncType = void(Args...);
|
|
using StdFuncType = std::function<void(Args...)>;
|
|
};
|
|
|
|
/**
|
|
* @brief 监听器模板类
|
|
*
|
|
* 构造时自动注册,析构时自动注销
|
|
*/
|
|
template<typename EHandler>
|
|
class Listener : public ListenerBase {
|
|
public:
|
|
using ArgTuple = typename EHandler::ArgTuple;
|
|
using StdFuncType = typename TupleExtractor<ArgTuple>::StdFuncType;
|
|
|
|
Listener();
|
|
~Listener();
|
|
|
|
template<typename Fn>
|
|
void bind(Fn&& func) {
|
|
callback_ = std::forward<Fn>(func);
|
|
}
|
|
|
|
void enable() { enabled_ = true; }
|
|
void disable() { enabled_ = false; }
|
|
bool isEnabled() const { return enabled_; }
|
|
void reset() { callback_ = nullptr; }
|
|
|
|
template<typename... Args>
|
|
void invoke(Args&&... args) {
|
|
if (callback_ && enabled_) {
|
|
callback_(std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
|
|
const char* busName() const { return EHandler::BUS_NAME; }
|
|
const char* eventName() const { return EHandler::NAME; }
|
|
|
|
private:
|
|
bool enabled_ = true;
|
|
StdFuncType callback_;
|
|
};
|
|
|
|
template<typename EHandler>
|
|
Listener<EHandler>::Listener() {
|
|
entry_ = new ListenerEntry();
|
|
entry_->listener = this;
|
|
ListenerDB<EHandler>::container()->addListener(this);
|
|
}
|
|
|
|
template<typename EHandler>
|
|
Listener<EHandler>::~Listener() {
|
|
ListenerDB<EHandler>::container()->removeListener(this);
|
|
}
|
|
|
|
#define EVENT_LIST_LOOP_BEGIN(curr, list) \
|
|
for (ListenerEntry* curr = list; curr != nullptr; curr = curr->next) {
|
|
|
|
#define EVENT_LIST_LOOP_END(curr, list) }
|
|
|
|
template<typename EHandler, typename... Args>
|
|
bool ListenerContainer::broadcast(Args&&... args) {
|
|
broadcasting_++;
|
|
|
|
EVENT_LIST_LOOP_BEGIN(curr, listenerList_)
|
|
if (curr->listener) {
|
|
static_cast<Listener<EHandler>*>(curr->listener)->invoke(std::forward<Args>(args)...);
|
|
}
|
|
EVENT_LIST_LOOP_END(curr, listenerList_)
|
|
|
|
broadcasting_--;
|
|
|
|
if (!broadcasting_ && hasPendingListeners()) {
|
|
processPendingListeners();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief 广播事件
|
|
*/
|
|
template<typename EHandler, typename... Args>
|
|
void broadcast(Args&&... args) {
|
|
static_assert(sizeof...(Args) == EHandler::ARG_COUNT, "Parameter count incorrect");
|
|
auto* container = ListenerDB<EHandler>::container();
|
|
container->template broadcast<EHandler, Args...>(std::forward<Args>(args)...);
|
|
}
|
|
|
|
} // namespace extra2d::event
|