文档概览
此文档详细介绍了 pa2d 库中的窗口管理系统,采用线程每窗口架构,支持多窗口、事件处理和渲染功能。Window 类提供完整的用户交互界面。
重要特性说明
线程每窗口架构: 每个窗口运行在独立的消息循环线程中
多窗口支持: 可同时创建和管理多个窗口
显式渲染: 窗口默认隐藏,需要调用 show() 显示和 render() 更新内容
避免并发访问: 避免跨线程并发访问 Canvas
裁剪处理: 调整窗口尺寸可能裁剪内容,需监听 onResize 事件重新渲染
主要分为四个核心模块:
Window 窗口类: 完整的窗口管理和渲染功能
事件结构体: 键盘和鼠标事件定义
句柄类型: Windows 系统句柄包装
系统函数: 全局系统信息查询
Window 窗口类
线程每窗口架构(后台消息循环),支持多窗口。窗口默认隐藏,必须显式调用 show() 和 render()。
构造与析构
| 方法 | 说明 |
|---|---|
Window(int width = -1, int height = -1, const char* title = "PA2D") |
构造指定尺寸和标题的窗口 |
Window(const Window& rhs) |
复制构造(支持窗口管理) |
Window(Window&& rhs) noexcept |
移动构造 |
~Window() |
析构函数(自动关闭窗口) |
Window& operator=(const Window& rhs) |
复制赋值 |
Window& operator=(Window&& rhs) noexcept |
移动赋值 |
句柄访问
| 方法 | 返回类型 | 说明 |
|---|---|---|
getHandle() const |
HWND |
获取底层窗口句柄 |
窗口状态
| 方法 | 返回类型 | 说明 |
|---|---|---|
width() const |
int |
获取窗口客户区宽度 |
height() const |
int |
获取窗口客户区高度 |
isOpen() const |
bool |
检查窗口是否打开 |
isClosed() const |
bool |
检查窗口是否已关闭 |
isVisible() const |
bool |
检查窗口是否可见 |
isMaximized() const |
bool |
检查窗口是否最大化 |
isMinimized() const |
bool |
检查窗口是否最小化 |
isFullscreen() const |
bool |
检查窗口是否全屏 |
窗口操作
| 方法 | 返回类型 | 说明 |
|---|---|---|
show() |
Window& |
显示窗口(必须调用) |
hide() |
Window& |
隐藏窗口 |
close() |
Window& |
关闭窗口 |
setVisible(bool visible) |
Window& |
设置窗口可见性 |
focus() |
Window& |
将焦点设置到窗口 |
阻塞等待
| 方法 | 说明 |
|---|---|
waitForClose() |
阻塞当前线程直到窗口关闭 |
尺寸与位置
| 方法 | 返回类型 | 说明 |
|---|---|---|
setPosition(int x, int y) |
Window& |
设置窗口位置(屏幕坐标) |
getPosition() const |
PointInt |
获取窗口位置 |
setClientSize(int width, int height) |
Window& |
设置客户区尺寸 |
getClientSize() const |
Size |
获取客户区尺寸 |
setWindowSize(int width, int height) |
Window& |
设置窗口整体尺寸(包括边框) |
getWindowSize() const |
Size |
获取窗口整体尺寸 |
渲染功能
| 方法 | 说明 |
|---|---|
render(const Canvas& canvas, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, int height = -1, bool clearBackground = true, COLORREF bgColor = 0) |
渲染 Canvas 到窗口 |
render(const Buffer& buffer, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1, int height = -1, bool clearBackground = true, COLORREF bgColor = 0) |
渲染 Buffer 到窗口 |
renderCentered(const Canvas& canvas, bool clearBackground = true, COLORREF bgColor = 0) |
居中渲染 Canvas |
renderCentered(const Buffer& buffer, bool clearBackground = true, COLORREF bgColor = 0) |
居中渲染 Buffer |
// 创建窗口
pa2d::Window window(800, 600, "PA2D 示例窗口");
// 创建画布并绘制内容
pa2d::Canvas canvas(800, 600, pa2d::White);
canvas.circle(400, 300, 200, pa2d::Red_fill + 2.0_w)
.textCentered(400, 300, L"Hello PA2D!", 36, pa2d::Black);
// 显示窗口(必须先显示)
window.show();
// 渲染内容到窗口
window.render(canvas);
// 更新内容并重新渲染
canvas.circle(400, 300, 150, pa2d::Blue_fill + 2.0_w);
window.render(canvas); // 需要再次调用 render() 更新显示
// 居中渲染
pa2d::Canvas smallCanvas(400, 300, pa2d::Green);
window.renderCentered(smallCanvas);
// 阻塞等待窗口关闭
window.waitForClose();
事件回调
| 方法 | 说明 |
|---|---|
onKey(KeyCallback cb) |
设置键盘事件回调 |
onMouse(MouseCallback cb) |
设置鼠标事件回调 |
onResize(ResizeCallback cb) |
设置窗口尺寸改变回调 |
onClose(CloseCallback cb) |
设置窗口关闭回调(返回 false 可阻止关闭) |
onChar(CharCallback cb) |
设置字符输入回调 |
onFocus(FocusCallback cb) |
设置焦点变化回调 |
onMenu(MenuCallback cb) |
设置菜单项点击回调 |
onFileDrop(FileListCallback cb) |
设置文件拖放回调 |
onClipboardFiles(FileListCallback cb) |
设置剪贴板文件变化回调 |
disableClipboardFiles() |
禁用剪贴板文件监听 |
pa2d::Window window(800, 600, "事件处理示例");
// 键盘事件处理
window.onKey([](const pa2d::KeyEvent& event) {
if (event.pressed) {
std::cout << "按键按下: " << event.key << std::endl;
if (event.key == VK_ESCAPE) {
std::cout << "ESC 键被按下" << std::endl;
}
} else {
std::cout << "按键释放: " << event.key << std::endl;
}
});
// 鼠标事件处理
window.onMouse([](const pa2d::MouseEvent& event) {
if (event.button == -2) { // 鼠标移动
std::cout << "鼠标移动到: (" << event.x << ", " << event.y << ")" << std::endl;
} else if (event.button == 0) { // 左键
if (event.pressed) {
std::cout << "左键按下 at (" << event.x << ", " << event.y << ")" << std::endl;
} else {
std::cout << "左键释放 at (" << event.x << ", " << event.y << ")" << std::endl;
}
} else if (event.button == -1) { // 滚轮
std::cout << "滚轮滚动: " << event.wheelDelta << std::endl;
}
});
// 窗口尺寸变化处理
window.onResize([](int width, int height) {
std::cout << "窗口尺寸改变: " << width << " x " << height << std::endl;
// 需要重新渲染内容以适应新尺寸
});
// 窗口关闭确认
window.onClose([]() {
std::cout << "确认关闭窗口吗?" << std::endl;
// 返回 false 可阻止窗口关闭
return true; // 允许关闭
});
// 字符输入
window.onChar([](wchar_t ch) {
std::wcout << L"输入字符: " << ch << std::endl;
});
// 文件拖放支持
window.enableFileDrop();
window.onFileDrop([](const std::vector& files) {
std::cout << "拖放了 " << files.size() << " 个文件:" << std::endl;
for (const auto& file : files) {
std::cout << " - " << file << std::endl;
}
});
window.show();
window.waitForClose();
输入状态
| 方法 | 返回类型 | 说明 |
|---|---|---|
getMousePosition() const |
Point |
获取鼠标在窗口内的位置 |
isMouseInWindow() const |
bool |
检查鼠标是否在窗口内 |
isMouseButtonPressed(int button) const |
bool |
检查鼠标按钮是否按下(0=左键,1=右键,2=中键) |
isKeyPressed(int vkCode) const |
bool |
检查指定虚拟键码是否按下 |
isShiftPressed() const |
bool |
检查 Shift 键是否按下 |
isCtrlPressed() const |
bool |
检查 Ctrl 键是否按下 |
isAltPressed() const |
bool |
检查 Alt 键是否按下 |
窗口控制
| 方法 | 返回类型 | 说明 |
|---|---|---|
maximize() |
Window& |
最大化窗口 |
minimize() |
Window& |
最小化窗口 |
restore() |
Window& |
恢复窗口正常状态 |
flash(bool flashTitleBar = true) |
Window& |
闪烁窗口标题栏或任务栏图标 |
setResizable(bool resizable) |
Window& |
设置窗口是否可调整大小 |
setAlwaysOnTop(bool onTop) |
Window& |
设置窗口是否始终置顶 |
setBorderless(bool borderless) |
Window& |
设置窗口无边框 |
setTitlebarless(bool titlebarless) |
Window& |
设置窗口无标题栏 |
setFullscreen(bool fullscreen) |
Window& |
设置全屏模式 |
setMinSize(int minWidth, int minHeight) |
Window& |
设置窗口最小尺寸 |
setMaxSize(int maxWidth, int maxHeight) |
Window& |
设置窗口最大尺寸 |
setMinimizeButton(bool show) |
Window& |
显示/隐藏最小化按钮 |
setMaximizeButton(bool show) |
Window& |
显示/隐藏最大化按钮 |
setCloseButton(bool show) |
Window& |
显示/隐藏关闭按钮 |
外观设置
| 方法 | 返回类型 | 说明 |
|---|---|---|
setTitle(const char* title) |
Window& |
设置窗口标题(多字节) |
setTitle(const wchar_t* title) |
Window& |
设置窗口标题(宽字符) |
getTitle() const |
std::string |
获取窗口标题 |
setCursor(HCURSOR cursor) |
Window& |
设置自定义光标 |
setCursorDefault() |
Window& |
设置默认箭头光标 |
setCursorWait() |
Window& |
设置等待(沙漏)光标 |
setCursorCross() |
Window& |
设置十字光标 |
setCursorHand() |
Window& |
设置手形光标 |
setCursorText() |
Window& |
设置文本输入(I-beam)光标 |
setCursorVisibility(bool visible) |
Window& |
设置光标可见性 |
setCursorPosition(int x, int y) |
Window& |
设置光标位置(窗口坐标) |
setIcon(HICON icon) |
Window& |
设置窗口图标 |
setIconFromResource(int resourceId) |
Window& |
从资源设置窗口图标 |
输入控制
| 方法 | 返回类型 | 说明 |
|---|---|---|
setMouseCapture(bool capture) |
Window& |
设置鼠标捕获(锁定鼠标到窗口) |
剪贴板
| 方法 | 返回类型 | 说明 |
|---|---|---|
setClipboardText(const std::string& text) |
bool |
设置剪贴板文本(多字节) |
getClipboardText() |
std::string |
获取剪贴板文本(多字节) |
setClipboardText(const std::wstring& text) |
bool |
设置剪贴板文本(宽字符) |
getClipboardTextW() |
std::wstring |
获取剪贴板文本(宽字符) |
hasClipboardText() const |
bool |
检查剪贴板是否有文本 |
getClipboardFiles() |
std::vector<std::string> |
获取剪贴板中的文件列表 |
hasClipboardFiles() const |
bool |
检查剪贴板是否有文件 |
enableFileDrop(bool enable = true) |
Window& |
启用文件拖放支持 |
菜单系统
| 方法 | 返回类型 | 说明 |
|---|---|---|
setMenu(HMENU menu) |
Window& |
设置窗口菜单 |
createMenu() |
HMENU |
创建菜单 |
createPopupMenu() |
HMENU |
创建弹出菜单 |
appendMenuItem(HMENU menu, const char* text, int id, bool enabled = true) |
Window& |
向菜单添加菜单项 |
appendMenuSeparator(HMENU menu) |
Window& |
向菜单添加分隔符 |
appendMenuPopup(HMENU menu, const char* text, HMENU popupMenu) |
Window& |
向菜单添加子菜单 |
destroyMenu(HMENU menu) |
Window& |
销毁菜单 |
pa2d::Window window(800, 600, "菜单示例");
// 创建主菜单
pa2d::Window::HMENU mainMenu = window.createMenu();
// 创建文件子菜单
pa2d::Window::HMENU fileMenu = window.createPopupMenu();
window.appendMenuItem(fileMenu, "新建(&N)", 1001)
.appendMenuItem(fileMenu, "打开(&O)", 1002)
.appendMenuSeparator(fileMenu)
.appendMenuItem(fileMenu, "保存(&S)", 1003)
.appendMenuItem(fileMenu, "另存为(&A)", 1004)
.appendMenuSeparator(fileMenu)
.appendMenuItem(fileMenu, "退出(&X)", 1005);
// 创建编辑子菜单
pa2d::Window::HMENU editMenu = window.createPopupMenu();
window.appendMenuItem(editMenu, "撤销(&U)", 2001)
.appendMenuItem(editMenu, "重做(&R)", 2002)
.appendMenuSeparator(editMenu)
.appendMenuItem(editMenu, "剪切(&T)", 2003)
.appendMenuItem(editMenu, "复制(&C)", 2004)
.appendMenuItem(editMenu, "粘贴(&P)", 2005);
// 将子菜单添加到主菜单
window.appendMenuPopup(mainMenu, "文件(&F)", fileMenu)
.appendMenuPopup(mainMenu, "编辑(&E)", editMenu);
// 设置窗口菜单
window.setMenu(mainMenu);
// 菜单项点击处理
window.onMenu([](int menuId) {
switch (menuId) {
case 1001: std::cout << "新建文件" << std::endl; break;
case 1002: std::cout << "打开文件" << std::endl; break;
case 1003: std::cout << "保存文件" << std::endl; break;
case 1005: std::cout << "退出程序" << std::endl; break;
case 2003: std::cout << "剪切" << std::endl; break;
case 2004: std::cout << "复制" << std::endl; break;
case 2005: std::cout << "粘贴" << std::endl; break;
}
});
window.show();
window.waitForClose();
// 清理菜单
window.destroyMenu(mainMenu);
事件结构体
定义键盘和鼠标事件的数据结构,用于事件回调函数。
KeyEvent 键盘事件
| 成员 | 类型 | 说明 |
|---|---|---|
key |
int |
虚拟键码(如 VK_ESCAPE, VK_SPACE 等) |
pressed |
bool |
按键状态:true=按下,false=释放 |
// Windows 虚拟键码(部分常用)
#include <windows.h> // 或包含 Windows 头文件以获取 VK_ 常量
#define VK_LBUTTON 0x01 // 鼠标左键
#define VK_RBUTTON 0x02 // 鼠标右键
#define VK_MBUTTON 0x04 // 鼠标中键
#define VK_BACK 0x08 // Backspace 键
#define VK_TAB 0x09 // Tab 键
#define VK_RETURN 0x0D // Enter 键
#define VK_SHIFT 0x10 // Shift 键
#define VK_CONTROL 0x11 // Ctrl 键
#define VK_MENU 0x12 // Alt 键
#define VK_ESCAPE 0x1B // Esc 键
#define VK_SPACE 0x20 // 空格键
#define VK_PRIOR 0x21 // Page Up
#define VK_NEXT 0x22 // Page Down
#define VK_END 0x23 // End 键
#define VK_HOME 0x24 // Home 键
#define VK_LEFT 0x25 // 左箭头
#define VK_UP 0x26 // 上箭头
#define VK_RIGHT 0x27 // 右箭头
#define VK_DOWN 0x28 // 下箭头
#define VK_INSERT 0x2D // Insert 键
#define VK_DELETE 0x2E // Delete 键
// 字母键 'A' - 'Z' 对应 0x41 - 0x5A
// 数字键 '0' - '9' 对应 0x30 - 0x39
// 功能键 F1 - F24 对应 0x70 - 0x87
MouseEvent 鼠标事件
| 成员 | 类型 | 说明 |
|---|---|---|
x, y |
int |
鼠标位置(窗口坐标) |
button |
int |
按钮标识符:0=左键, 1=右键, 2=中键, -1=滚轮, -2=移动, -3=离开 |
pressed |
bool |
按钮按下/释放状态(对滚轮和移动事件无效) |
wheelDelta |
int |
滚轮增量(正值向前,负值向后) |
事件回调类型
| 回调类型 | 函数签名 | 说明 |
|---|---|---|
KeyCallback |
std::function<void(const KeyEvent&)> |
键盘事件回调 |
MouseCallback |
std::function<void(const MouseEvent&)> |
鼠标事件回调 |
ResizeCallback |
std::function<void(int, int)> |
窗口尺寸改变回调 |
CloseCallback |
std::function<bool()> |
窗口关闭回调(返回 false 阻止关闭) |
CharCallback |
std::function<void(wchar_t)> |
字符输入回调 |
FocusCallback |
std::function<void(bool)> |
焦点变化回调(true=获得焦点,false=失去焦点) |
MenuCallback |
std::function<void(int)> |
菜单项点击回调 |
FileListCallback |
std::function<void(const std::vector<std::string>&)> |
文件列表回调(用于拖放和剪贴板) |
句柄类型
Windows 系统句柄的包装类型,避免与 windows.h 头文件冲突。
窗口句柄类型
| 类型别名 | 底层类型 | 说明 |
|---|---|---|
HWND |
void* |
窗口句柄 |
HMENU |
void* |
菜单句柄 |
HICON |
void* |
图标句柄 |
HCURSOR |
void* |
光标句柄 |
COLORREF |
unsigned long |
颜色引用(RGB 值) |
// 获取窗口句柄
pa2d::Window window(800, 600);
pa2d::Window::HWND hwnd = window.getHandle();
// 使用系统函数时需要类型转换
#ifdef _WIN32
#include <windows.h>
// 将 pa2d::Window::HWND 转换为 Windows HWND
HWND winHwnd = static_cast<HWND>(hwnd);
// 使用 Windows API
SetWindowTextW(winHwnd, L"新的窗口标题");
// 将 Windows COLORREF 转换为 pa2d::Window::COLORREF
pa2d::Window::COLORREF bgColor = RGB(255, 0, 0); // 红色背景
// 渲染时使用颜色
window.render(canvas, 0, 0, 0, 0, -1, -1, true, bgColor);
#endif
系统函数
全局系统信息查询函数。
| 函数 | 返回类型 | 说明 |
|---|---|---|
getGlobalMousePosition() |
Point |
获取全局鼠标位置(屏幕坐标) |
getScreenSize() |
Point |
获取屏幕尺寸(像素) |
getWorkAreaSize() |
Point |
获取工作区尺寸(排除任务栏) |
getDpiScale() |
double |
获取 DPI 缩放比例 |
// 获取系统信息
pa2d::Point mousePos = pa2d::getGlobalMousePosition();
std::cout << "全局鼠标位置: (" << mousePos.x << ", " << mousePos.y << ")" << std::endl;
pa2d::Point screenSize = pa2d::getScreenSize();
std::cout << "屏幕尺寸: " << screenSize.x << " x " << screenSize.y << std::endl;
pa2d::Point workArea = pa2d::getWorkAreaSize();
std::cout << "工作区尺寸: " << workArea.x << " x " << workArea.y << std::endl;
double dpiScale = pa2d::getDpiScale();
std::cout << "DPI 缩放比例: " << dpiScale << std::endl;
// 根据屏幕尺寸创建窗口
int windowWidth = static_cast(screenSize.x * 0.8);
int windowHeight = static_cast(screenSize.y * 0.8);
pa2d::Window window(windowWidth, windowHeight, "自适应窗口");
// 将窗口居中
int centerX = static_cast((screenSize.x - windowWidth) / 2);
int centerY = static_cast((screenSize.y - windowHeight) / 2);
window.setPosition(centerX, centerY);
window.show();
window.waitForClose();