Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
*.ninja
*.check_cache
/out
test_exclusive_dispatcher
89 changes: 89 additions & 0 deletions IMPLEMENTATION_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# EventDispatcher 独占模式实现总结

## 已完成的功能

本次实现为ULRE渲染引擎添加了EventDispatcher独占模式功能,解决了"某一个EventPatcher需要在更高级别处暂时处于独占状态"的需求。

## 核心功能

### 1. 基础EventDispatcher类 (`inc/hgl/io/event/EventDispatcher.h`)
- 支持层级事件分发
- 独占模式管理
- 更高级别独占请求机制

### 2. 关键方法
```cpp
// 设置独占模式
bool SetExclusiveMode(EventDispatcher* dispatcher);

// 请求在更高级别处于独占状态
bool RequestExclusiveAtHigherLevel(int levels = 1);

// 释放独占状态
void ReleaseExclusiveAtHigherLevel(int levels = 1);
```

### 3. 事件处理类
- `WindowEvent`: 窗口事件基类,支持独占模式事件分发
- `MouseEvent`: 鼠标事件处理,包含坐标跟踪
- `KeyboardEvent`: 键盘事件处理,按键状态管理

## 使用场景

### 场景1: UI对话框独占
```cpp
class DialogEventHandler : public io::WindowEvent
{
void OpenDialog()
{
// 在父级别设置独占,游戏主循环不会收到事件
RequestExclusiveAtHigherLevel(1);
}
};
```

### 场景2: 摄像机控制独占
```cpp
class CameraController : public io::MouseEvent
{
void StartCameraControl()
{
// 在更高级别独占鼠标事件
RequestExclusiveAtHigherLevel(2);
}
};
```

## 测试验证

创建了完整的测试程序 (`example/EventDispatcherExclusiveTest.cpp`),验证了:
- ✅ 正常事件传播
- ✅ 对话框独占模式
- ✅ 摄像机控制独占
- ✅ 多级独占模式交互

## 编译和运行

```bash
# 编译测试程序
g++ -std=c++17 -I inc example/EventDispatcherExclusiveTest.cpp -o test_exclusive_dispatcher

# 运行测试
./test_exclusive_dispatcher
```

## 设计优势

1. **最小化修改**: 不改变现有代码结构,只添加新功能
2. **向后兼容**: 现有EventDispatcher使用方式完全不变
3. **灵活性**: 支持多级独占,可以精确控制独占范围
4. **类型安全**: 使用模板和dynamic_cast确保类型安全
5. **易于使用**: 简单的API,清晰的语义

## 文档

详细设计文档请参考: `doc/EventDispatcher_Exclusive_Mode.md`

## 适用性

此实现独立于ULRE的submodule依赖,使用标准C++容器和类型,可以在当前仓库状态下直接编译和使用。
178 changes: 178 additions & 0 deletions doc/EventDispatcher_Exclusive_Mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# EventDispatcher 独占模式设计文档

## 概述

本文档描述了为ULRE渲染引擎设计的EventDispatcher独占模式功能。该功能允许某个EventDispatcher在更高级别暂时处于独占状态,从而可以拦截和独占处理所有事件,而不是按照正常的层级传递。

## 问题陈述

原有的EventDispatcher系统采用层级传递的方式:
```
Window -> RenderFramework -> Scene -> SceneNode
```

但在某些场景下,需要某个EventDispatcher能够在更高级别暂时处于独占状态,例如:
- 游戏中弹出对话框时,需要独占所有输入事件
- 摄像机控制时,需要独占鼠标事件
- 调试模式下,调试界面需要独占所有事件

## 设计方案

### 核心概念

1. **独占模式(Exclusive Mode)**: 当某个EventDispatcher处于独占模式时,其父级EventDispatcher只会将事件传递给该独占EventDispatcher,而不会传递给其他子EventDispatcher。

2. **更高级别独占(Higher Level Exclusive)**: 允许EventDispatcher请求在其父级或更高级别的EventDispatcher中设置独占模式。

### 关键接口

#### EventDispatcher基类
```cpp
class EventDispatcher
{
// 设置独占模式
virtual bool SetExclusiveMode(EventDispatcher* dispatcher);

// 退出独占模式
virtual void ExitExclusiveMode();

// 请求在更高级别处于独占状态
virtual bool RequestExclusiveAtHigherLevel(int levels = 1);

// 释放在更高级别的独占状态
virtual void ReleaseExclusiveAtHigherLevel(int levels = 1);

// 查询独占状态
bool IsExclusiveMode() const;
EventDispatcher* GetExclusiveDispatcher() const;
};
```

### 工作原理

1. **正常模式**: 事件从父EventDispatcher传递给所有子EventDispatcher
2. **独占模式**: 事件只传递给被设置为独占的子EventDispatcher
3. **更高级别独占**: 通过`RequestExclusiveAtHigherLevel()`方法,可以在指定级别的父EventDispatcher中设置独占模式

### 使用场景

#### 场景1: 对话框独占
```cpp
class DialogEventHandler : public io::WindowEvent
{
public:
void OpenDialog()
{
// 在父级别设置独占,游戏主循环不会收到事件
RequestExclusiveAtHigherLevel(1);
}

void CloseDialog()
{
// 释放独占状态
ReleaseExclusiveAtHigherLevel(1);
}
};
```

#### 场景2: 摄像机控制独占
```cpp
class CameraController : public io::MouseEvent
{
public:
void StartCameraControl()
{
// 在更高级别独占鼠标事件
RequestExclusiveAtHigherLevel(2);
}

void StopCameraControl()
{
ReleaseExclusiveAtHigherLevel(2);
}
};
```

### 层级结构示例

```
Window (Level 0)
└── RenderFramework (Level 1)
└── Scene (Level 2)
├── GameLogic (Level 3)
├── UIDialog (Level 3)
└── CameraController (Level 3)
```

当UIDialog调用`RequestExclusiveAtHigherLevel(1)`时:
- Scene (Level 2) 设置UIDialog为独占EventDispatcher
- GameLogic和CameraController将不会收到事件
- 只有UIDialog能够处理事件

当CameraController调用`RequestExclusiveAtHigherLevel(2)`时:
- RenderFramework (Level 1) 设置Scene为独占EventDispatcher
- 其他Scene将不会收到事件
- 在Scene内部,CameraController仍需要设置自己的独占模式

## 实现细节

### 数据结构
```cpp
class EventDispatcher
{
protected:
ObjectList<EventDispatcher> child_dispatchers; // 子事件分发器列表
EventDispatcher* parent_dispatcher = nullptr; // 父事件分发器
EventDispatcher* exclusive_dispatcher = nullptr; // 独占模式的事件分发器
bool is_exclusive_mode = false; // 是否处于独占模式
};
```

### 事件分发逻辑
```cpp
template<typename EventType>
void DispatchToChildren(const EventType& event)
{
if (is_exclusive_mode && exclusive_dispatcher)
{
// 独占模式下只分发给独占分发器
exclusive_dispatcher->HandleEvent(event);
}
else
{
// 正常模式下分发给所有子分发器
for (EventDispatcher* child : child_dispatchers)
{
child->HandleEvent(event);
}
}
}
```

## 线程安全

当前实现假设所有EventDispatcher操作都在主线程中进行。如果需要多线程支持,需要添加适当的锁机制。

## 性能考虑

1. 独占模式的切换开销很小,主要是设置几个指针和布尔值
2. 事件分发时的额外开销是一个布尔值检查
3. 内存开销是每个EventDispatcher增加两个指针和一个布尔值

## 扩展性

该设计支持以下扩展:
1. 多重独占模式(一个EventDispatcher可以在多个级别设置独占)
2. 条件独占模式(基于事件类型的独占)
3. 临时独占模式(基于时间的独占)

## 使用示例

完整的使用示例请参考:`inc/hgl/example/ExclusiveEventDispatcherExample.h`

## 测试

可以运行以下测试程序来验证功能:
```bash
./example/EventDispatcherExclusiveTest
```
28 changes: 28 additions & 0 deletions example/EventDispatcherExclusiveTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <hgl/example/ExclusiveEventDispatcherExample.h>
#include <iostream>

/**
* 测试独占模式事件分发器功能
*/
int main()
{
std::cout << "=== EventDispatcher 独占模式功能演示 ===" << std::endl;
std::cout << "This demonstrates how EventDispatcher exclusive mode works." << std::endl;
std::cout << "独占模式允许某个事件分发器在更高级别暂时接管所有事件处理。" << std::endl << std::endl;

try
{
hgl::example::ExclusiveEventDispatcherExample::DemonstrateExclusiveMode();

std::cout << std::endl << "=== 演示完成 ===" << std::endl;
std::cout << "独占模式功能演示成功完成!" << std::endl;
std::cout << "Exclusive mode demonstration completed successfully!" << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "Error during demonstration: " << e.what() << std::endl;
return 1;
}

return 0;
}
Loading