# API 文档 [English API Documentation](API Documentation.en.md) 下面是 **StellarX GUI 框架** 各主要类和函数的 API 文档。每个类的用途、接口、参数和返回值等详细信息如下: ## Control 类 (抽象基类) **描述:** `Control` 是所有控件的抽象基类,定义了通用的属性和接口,包括位置、尺寸、重绘标记等基础功能。它提供绘图状态的保存与恢复机制,确保控件的绘制操作不影响全局绘图状态。此外还声明了一系列供子类实现的纯虚函数(如 `draw()` 和 `handleEvent()`),并禁止拷贝(支持移动语义)以防止意外的复制开销。一般情况下,`Control` 不直接实例化,而作为其他具体控件的父类存在。 **特性:** - 定义控件的基本属性(坐标、尺寸、可见性、脏标记等)并提供对应的存取方法。 - 提供绘图状态管理接口(`saveStyle()`/`restoreStyle()` 等)用于在控件绘制前后保存和恢复全局画笔状态。 - 声明纯虚接口,如 `draw()`(绘制控件)和 `handleEvent()`(处理事件),所有具体控件都需实现。 - 提供移动构造和移动赋值支持,防止控件被不小心拷贝。 ### 公共成员函数 #### Control::updateBackground() - **用途:** 释放之前保存的背景快照并重新捕获当前背景,用于在控件尺寸变化或内容变化后更新其背景缓存。调用此函数可确保控件背景的正确性,避免因为尺寸变化导致的背景错位。 - **参数:** 无。 - **返回值:** 无。 - **行为细节:** 默认实现中,`updateBackground()` 会先丢弃旧的背景位图(相当于调用内部的 `discardBackground()`),然后在控件当前位置和新尺寸范围内重新保存一份背景快照。这通常在控件大小改变或重绘需求发生时自动调用,一般不需要用户手动调用。 - **示例:** 假设有自定义控件在大小调整时需要刷新背景,可以在调整尺寸后调用: ```c++ myControl.setWidth(newW); myControl.setHeight(newH); myControl.updateBackground(); // 更新背景快照以匹配新尺寸 ``` #### Control::onWindowResize() - **用途:** 当父窗口尺寸发生变化时,通知控件进行相应处理的事件函数。用于在窗口大小改变时调整控件状态,例如作废过期的背景缓存。 - **参数:** 无。 - **返回值:** 无。 - **行为细节:** 默认实现中,`onWindowResize()` 会调用 `discardBackground()` 丢弃控件保存的背景位图,以防止窗口尺寸变化后背景错位。容器控件会重写该方法(例如 `Canvas` 会在自身处理后继续调用子控件的 `onWindowResize()`)。一般用户不需要主动调用此方法——框架会在窗口或容器尺寸变化时自动触发调用。 - **示例:** 自定义控件如需在窗口大小改变时执行特定逻辑,可以重写此函数: ```c++ void MyControl::onWindowResize() { Control::onWindowResize(); // 调用基类以丢弃背景等 // 自定义逻辑,例如根据新窗口尺寸调整位置 repositionBasedOnNewWindowSize(); } ``` #### Control::getX() / getY() / getWidth() / getHeight() - **用途:** 获取控件的全局坐标位置(左上角 `x, y`)或当前尺寸(宽度、高度)。 - **参数:** 无。 - **返回值:** 分别返回控件的全局 X 坐标、全局 Y 坐标、当前宽度、高度,类型均为 `int`。 - **行为细节:** 这些方法提供控件在窗口坐标系下的位置和大小,通常用于布局计算或调试。注意,全局坐标是相对于整个应用窗口的原点(0,0)。 - **示例:** ```c++ int x = ctrl.getX(); int h = ctrl.getHeight(); std::cout << "Control at X = " << x << ", height = " << h << std::endl; ``` #### Control::getRight() / getBottom() - **用途:** 获取控件的右边缘的 X 坐标 (`getRight()`) 和下边缘的 Y 坐标 (`getBottom()`),以全局坐标计算。 - **参数:** 无。 - **返回值:** 控件右边缘 X 坐标、底边缘 Y 坐标,类型为 `int`。 - **行为细节:** 这两个方法相当于 `getX() + getWidth()` 和 `getY() + getHeight()`,用于方便地获取控件在窗口坐标系中覆盖的边界位置。 - **示例:** ```c++ if (cursorX < ctrl.getRight() && cursorY < ctrl.getBottom()) { // 光标在ctrl控件矩形范围内 } ``` #### Control::getLocalX() / getLocalY() / getLocalWidth() / getLocalHeight() - **用途:** 获取控件相对于父容器坐标系的位置 (`LocalX, LocalY`) 以及在父容器内的尺寸 (`LocalWidth, LocalHeight`)。 - **参数:** 无。 - **返回值:** 控件相对父容器的 X 坐标、Y 坐标、宽度、高度,类型均为 `int`。 - **行为细节:** 当控件被添加到容器(例如 `Canvas`)时,其本地坐标以容器的左上角为原点。如果控件没有父容器(直接属于窗口),则本地坐标与全局坐标相同。这些方法对于在容器内部布局控件特别有用。 - **示例:** 假设有一个容器 `canvas`,其中包含子控件 `childCtrl`: ```c++ int localX = childCtrl.getLocalX(); int globalX = childCtrl.getX(); std::cout << "Child global X: " << globalX << ", local X in canvas: " << localX << std::endl; ``` #### Control::getLocalRight() / getLocalBottom() - **用途:** 获取控件相对于父容器坐标系的右边缘和底边缘坐标。 - **参数:** 无。 - **返回值:** 控件在父容器内的右侧坐标、下侧坐标,类型为 `int`。 - **行为细节:** 分别等价于 `getLocalX() + getLocalWidth()` 和 `getLocalY() + getLocalHeight()`。当需要确定控件在容器内部的边界位置时,这些方法提供了便利。 - **示例:** 可用于判断子控件是否超出了容器范围: ```c++ if (childCtrl.getLocalRight() > canvas.getWidth()) { // 子控件在容器内的右边缘超出了容器宽度 } ``` #### Control::setX(int x) / setY(int y) / setWidth(int w) / setHeight(int h) - **用途:** 设置控件的全局坐标位置(左上角 X 或 Y)或调整控件的尺寸(宽度或高度)。 - **参数:** `x` 或 `y` 为新的全局坐标;`w` 为新的宽度值;`h` 为新的高度值(类型均为 `int`)。 - **返回值:** 无。 - **行为细节:** 调用这些方法将直接修改控件的位置或大小,并自动将控件标记为“需要重绘”(内部将 `dirty` 标志置为 `true`),使得下一轮刷新时控件会按照新位置或新尺寸绘制。需要注意,如果控件在容器中,设置全局坐标会改变其在窗口中的位置,但相对父容器的位置也会随之更新。 - **示例:** 将控件 `ctrl` 移动到窗口坐标(100,100),并调整宽度为200: ```c++ ctrl.setX(100); ctrl.setY(100); ctrl.setWidth(200); // 高度保持不变,如果需要也可调用 ctrl.setHeight(newHeight); ``` #### Control::draw() (纯虚函数) - **用途:** 绘制控件的抽象接口,由具体控件类实现实际的绘图逻辑。 - **参数:** 无(绘制所需信息由控件内部状态确定)。 - **返回值:** 无。 - **行为细节:** `draw()` 会在框架重绘时由系统调用,具体控件应在实现中完成自身的绘图工作。通常一个 `draw()` 实现需要先调用 `Control::saveStyle()` 保存当前绘图状态,然后设置绘图颜色/字体等并绘制,再调用 `Control::restoreStyle()` 恢复状态。**用户不应直接调用**此方法;若要强制重绘,可以将控件标记为脏 (`setDirty(true)`) 并等待系统调用。 - **示例:** 以下为自定义控件实现 `draw()` 的示例伪代码: ```c++ void MyControl::draw() { saveStyle(); setfillcolor(backgroundColor); bar(x, y, x+width, y+height); // 绘制背景矩形 // ... 其他绘制 ... restoreStyle(); } ``` #### Control::handleEvent(const ExMessage& msg) (纯虚函数) - **用途:** 事件处理抽象接口,由具体控件实现,用于处理鼠标、键盘等交互事件。 - **参数:** `msg` 为 EasyX 框架的事件消息 (`ExMessage`),包含了事件类型、鼠标坐标、按键等信息。 - **返回值:** 布尔值,`true` 表示该事件已被控件处理(“消费”),不需要继续传递;`false` 表示控件未处理该事件,事件可交由其它对象或默认逻辑处理。 - **行为细节:** `handleEvent()` 方法同样由系统在事件循环中自动调用,用于让控件响应用户输入。不同控件根据需要处理不同类型的事件(如按钮处理鼠标点击,文本框处理键盘输入)。如果控件识别并处理了某个事件(例如鼠标点击在按钮范围内),应该返回 `true`,表示事件到此为止,不再向上传播。对于未处理的事件则返回 `false`。 - **示例:** 简化的按钮控件事件处理逻辑: ```c++ bool Button::handleEvent(const ExMessage& msg) { if(msg.message == WM_LBUTTONDOWN && isPointInButton(msg.x, msg.y)) { // 处理按钮点击 onClickCallback(); // 调用已注册的回调 return true; } return false; } ``` #### Control::setIsVisible(bool show) - **用途:** 设置控件的可见状态,动态显示或隐藏控件。传入 `false` 将隐藏控件,传入 `true` 则显示控件。 - **参数:** `show` 为布尔值,指定是否让控件可见。`true` 表示可见,`false` 表示隐藏。 - **返回值:** 无。 - **行为细节:** 该方法允许在运行时切换控件的显示状态。调用后控件的 `IsVisible()` 状态将改变,并自动触发重绘逻辑:隐藏时控件内容不再绘制;重新显示时控件会被标记需要重绘。对于容器控件(如 `Canvas`),本方法已被重写以**递归影响其子控件**:隐藏一个容器时,其所有子控件也会被隐藏;再次显示容器时,子控件也随之显示。这简化了整组界面的显隐控制。 - **示例:** ```c++ button.setIsVisible(false); // 隐藏一个按钮 // ... 某些操作后 ... button.setIsVisible(true); // 再次显示按钮 ``` #### Control::setParent(Control* parent) - **用途:** 设置控件的父容器指针。通常由容器在添加控件时调用,手动调用情形较少。 - **参数:** `parent` 指向新的父容器控件 (`Canvas` 等) 的指针。传入 `nullptr` 则表示清除父容器关联。 - **返回值:** 无。 - **行为细节:** 该方法将内部的 `parent` 指针设置为指定容器,并相应调整控件的坐标解释方式(有父容器时本地坐标相对于父容器)。在将控件添加到某个容器时,框架会自动调用此函数,无需用户干预。**注意:** 将控件从一个容器转移到另一个时,除了调用 `setParent()` 外,还需要确保在旧容器中移除、在新容器中添加控件(使用容器的 `addControl()` 等),否则界面状态不一致。 - **示例:** ```c++ canvas1.addControl(std::move(ctrl)); // 内部自动 setParent(&canvas1) // 如需从 canvas1 转移到 canvas2: canvas1.removeControl(ctrlPtr); canvas2.addControl(std::move(ctrlPtr)); // 内部 setParent(&canvas2) ``` #### Control::setDirty(bool dirty) - **用途:** 手动设置控件的“需要重绘”标记。当控件的内容或属性改变且希望立即刷新显示时,可调用此函数。 - **参数:** `dirty` 布尔值,`true` 表示标记控件需要重绘,`false` 表示清除重绘标记。 - **返回值:** 无。 - **行为细节:** 将 `dirty` 设置为 `true` 后,会在下一次屏幕刷新时调用控件的 `draw()` 重新绘制。通常框架内部在控件属性变化时已经设置了该标记,例如调用 `setX()/setWidth()` 等修改位置尺寸的方法后,控件已被标记脏。开发者也可以在自定义场景下调用它,例如控件内部状态变化但没有对应的 setter 方法时。一般不需要将 `dirty` 手动置为 `false`,因为重绘后框架会自动清除该标记。 - **示例:** ```c++ ctrl.setDirty(true); // 请求ctrl控件重绘 ``` #### Control::IsVisible() const - **用途:** 检查控件当前是否处于可见状态。 - **参数:** 无。 - **返回值:** `bool`,当前控件是否可见。`true` 表示可见,`false` 表示被隐藏。 - **行为细节:** 该方法返回控件内部 `show` 标志位的状态。初始情况下控件默认为可见 (`true`),如果通过 `setIsVisible(false)` 或控件所属父容器被隐藏,则此方法返回 `false`。当控件不可见时,其 `draw()` 将不会被调用。 - **示例:** ```c++ if (!ctrl.IsVisible()) { ctrl.setIsVisible(true); // 确保控件可见 } ``` ## Window 类 (应用主窗口) **描述:** `Window` 类代表应用程序的主窗口,它管理窗口的创建、消息循环以及全局的控件容器。`Window` 既负责初始化图形窗口(基于 EasyX),也是所有顶层控件的载体(充当它们的父级容器)。它处理系统事件的分发、定时重绘等,使用户只需关注向窗口添加控件和对事件作出响应。一个应用典型地只创建一个 `Window` 实例。 **特性:** - 提供多种窗口初始化模式(如是否双缓冲、是否显示控制台等)以适应不同应用需求。 - 支持设置窗口背景颜色或背景图片,以及窗口标题。 - 内置**对话框管理**系统:跟踪已弹出的模态/非模态对话框,避免重复弹出相同对话框。 - 集成完整的消息处理循环,自动分发事件给各控件的 `handleEvent()` 方法,并调度重绘各控件的 `draw()`。 - 管理所有添加到窗口的控件和对话框的生命周期,在窗口销毁时自动清理。 ### 公共成员函数 #### Window(int width, int height, int mode, COLORREF bkColor = BLACK, std::string headline = "窗口") (构造函数) - **用途:** 创建一个指定大小的应用程序窗口,并进行必要的图形初始化。可以指定窗口模式、背景颜色和窗口标题。 - **参数:** - `width`:窗口宽度(像素)。 - `height`:窗口高度(像素)。 - `mode`:窗口模式标志(int 类型)。不同的值可配置窗口属性,例如是否使用**双缓冲**绘图、是否显示控制台窗口等。典型地,0 表示默认(窗口模式,隐藏控制台),也可以使用 EasyX 提供的常量组合此参数,如 `EW_SHOWCONSOLE` 显示控制台,`EW_NOCLOSE` 禁止关闭按钮等。 - `bkColor` *(可选)*:窗口背景色,默认为黑色 (`BLACK`)。 - `headline` *(可选)*:窗口标题文本,默认为“窗口”。 - **返回值:** 无(构造函数)。 - **行为细节:** 构造 `Window` 对象时,会调用 EasyX 的图形初始化函数 `initgraph(width, height, mode)` 创建窗体,并自动应用提供的背景色以及设置窗口标题。如果模式开启了双缓冲,则框架会采用手动刷新机制。创建窗口后,**必须调用** `runEventLoop()` 进入消息循环,窗口才开始响应事件和绘制。 - **示例:** ```c++ // 创建800x600的窗口,双缓冲且显示控制台,背景为白色,标题为“示例” int mode = EW_SHOWCONSOLE | EW_NOBORDER; // 仅示例模式组合 Window mainWin(800, 600, mode, WHITE, "示例"); mainWin.draw(); mainWin.runEventLoop(); ``` #### Window::~Window() (析构函数) - **用途:** 销毁窗口对象并清理资源。 - **参数:** 无。 - **返回值:** 无。 - **行为细节:** 当 `Window` 对象被销毁时,框架会自动关闭图形窗口、释放 EasyX 图形资源,并清理窗口中托管的控件和对话框对象。通常不需要显式调用,直接让 `Window` 对象离开作用域或程序结束时由系统回收。 - **示例:** ```c++ { Window win(400, 300, 0); // ... 使用 win ... } // 作用域结束,自动析构,窗口关闭 ``` #### Window::runEventLoop() - **用途:** 进入窗口消息处理循环,启动 GUI 主事件循环。调用此函数后,窗口开始响应用户交互并持续运行,直到窗口关闭。 - **参数:** 无。 - **返回值:** `int`,窗口消息循环的退出代码(通常可直接返回给 `main()`)。 - **行为细节:** 该函数内部实现一个典型的事件循环:不断调用 EasyX 的 `peekmessage()` 获取事件消息,并将其分发给窗口内各控件的 `handleEvent()` 方法处理。同时,它负责按需重绘:每帧遍历所有控件调用其 `draw()` 方法。在循环中窗口也会检查模态对话框的存在并阻止底层控件交互等。**必须**在创建窗口并添加控件后调用此方法,否则窗口将一片空白且无法交互。 - **示例:** ```c++ Window win(640, 480, EW_SHOWCONSOLE, LIGHTGRAY, "Demo"); // ... 添加控件等初始化 ... wni.draw(); return win.runEventLoop(); // 进入事件循环,阻塞直到窗口关闭 ``` #### Window::addControl(std::unique_ptr control) - **用途:** 将一个新控件添加到窗口中进行管理和显示。可以添加诸如按钮、文本框、容器等任何继承自 `Control` 的控件。 - **参数:** `control` 为一个指向待添加控件的 `unique_ptr` 智能指针。函数内部会接管该指针的所有权。 - **返回值:** 无。 - **行为细节:** 添加控件时,窗口会将控件纳入内部维护的控件列表中,不会i调用控件的 `setParent()` 。此后框架每次重绘和事件处理都会考虑该控件。控件在窗口坐标系中的初始位置和尺寸取决于控件自身的属性。如果需要移除控件,目前可以通过 `clearAllControls()`(在 `Canvas` 中)或销毁窗口实现。 - **示例:** ```c++ auto btn = std::make_unique