6 Commits

Author SHA1 Message Date
0c1cf2938f feat: add a new awesome feature 2025-12-20 17:29:03 +08:00
aa0fa8d320 feat: add a new awesome feature 2025-12-05 20:05:10 +08:00
43564ef675 feat: add a new awesome feature 2025-12-04 14:25:56 +08:00
53dc237e46 feat: add a new awesome feature 2025-11-30 20:09:16 +08:00
46febdb973 feat: add a new awesome feature 2025-11-30 19:05:58 +08:00
f05962954f feat: add a new awesome feature 2025-11-20 01:59:53 +08:00
30 changed files with 2249 additions and 2027 deletions

View File

@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[中文文档](CHANGELOG.md) [中文文档](CHANGELOG.md)
## [v2.3.2] - 2025 - 12 - 20
### ✨ Added
- **Table: runtime reset for headers and data:** added `Table::clearHeaders()`, `Table::clearData()`, and `Table::resetTable()`. This allows a single `Table` instance to dynamically update its headers/data at runtime, and triggers the required recalculation (cell sizing / pagination info) and redraw.
- **TextBox: password mode:** added `PASSWORD_MODE` to `TextBoxmode`. User input is stored internally, while the render layer displays masked characters (e.g., `*`). The real text can be retrieved via `TextBox::getText()`.
### ⚙️ Changed
- **TabControl: clarified default active page semantics:**
- Calling `TabControl::setActiveIndex()` **before the first draw** now only records the default active index; it no longer immediately triggers the tab button click callback.
- **After the first draw completes**, if a default active index was set, the active state is applied and the active page is drawn (if the index is out of range, the last page is activated by default).
- Calling `TabControl::setActiveIndex()` **during runtime (non-first draw)** switches the active page immediately when the index is valid; out-of-range indices are ignored.
### ✅ Fixed
- **TabControl::setActiveIndex crash when called before drawing:** fixed a null-pointer access caused by triggering the tab button click callback before initialization. The default activation is now applied after the first draw completes, preventing crashes and ensuring the active page is rendered on first draw.
- **TabControl rendering glitches when toggling visibility (hidden -> visible):** fixed multi-page overlap/ghosting caused by non-active pages being incorrectly drawn after `setIsVisible(false) -> setIsVisible(true)`. Now, when TabControl is visible, only the active page is visible/drawable; if there is no active page, nothing is drawn.
## [v2.3.1] - 2025-11-30
### 🙏 Acknowledgements
- Special thanks to user [@To-KongBai](https://github.com/To-KongBai) for providing stable reproduction steps and key phenomenon comparisons (container nested child control coordinate transformation issue), which helped us quickly identify and fix the control coordinate transformation problem in deeply nested containers. ([Issues#6](https://github.com/Ysm-04/StellarX/issues/6))
- In the upcoming website (currently undergoing ICP filing), we plan to add a contributors' wall to acknowledge users. We welcome everyone to report bugs or share interfaces created with StellarX, and we will carefully read and include acknowledgements.
- Sincere thanks to every user who reports bugs—your feedback makes StellarX more stable and robust.
### ⚙️ Changes
- **Dialog Background Snapshot Mechanism:** `Dialog` no longer captures and destroys its own snapshot. The methods for capturing and restoring snapshots have been **removed** from `Dialog`, and the base class `Canvas` now handles all snapshot management. The `draw` method in `Dialog` no longer deals with snapshots.
- **Timing Adjustment for Window and Control Redrawing on Size Change:** In the main event loop, when the window size changes, control sizes are processed first. Old snapshots are discarded, followed by the redrawing of the window with the new size, and finally, the controls are redrawn.
### ✅ Fixes
- **Child Control Coordinate Transformation in Nested Containers:** `Canvas` overrides the base class's `setX/Y` methods to synchronize the global coordinates of child controls when the parent container's global coordinates change. This prevents child controls in nested containers from incorrectly treating the container's relative coordinates as global coordinates.
- **Solid Background Window Title Not Applied:** In the `Window`'s `draw()` method, the window title is forcibly set to ensure that the title passed when creating the window is applied correctly, preventing the issue where the window title doesn't take effect.
- **Tab Control Background Residue when Changing Coordinates:** In the overridden `setX/Y` methods of `TabControl`, all tabs and their child controls are forced to discard snapshots when the tab's coordinates change. This prevents background residue caused by incorrect snapshot restoration order after modifying coordinates.
- **Table Page Number Label and Pagination Button Misalignment when Changing Coordinates:** In `Table`'s `setX/Y`, the `isNeedButtonAndPageNum` state is reset to `true`, ensuring that the pagination button and page number label are recalculated and remain centered directly beneath the table when redrawn.
## [v2.3.0] - 2025-11-18 ## [v2.3.0] - 2025-11-18
### ✨ Added ### ✨ Added

View File

@@ -7,6 +7,49 @@ StellarX 项目所有显著的变化都将被记录在这个文件中。
[English document](CHANGELOG.en.md) [English document](CHANGELOG.en.md)
## [v2.3.2] - 2025 - 12 - 20
### ✨ 新增
- **Table 支持运行期重置表头与数据:**新增 `Table::clearHeaders()``Table::clearData()``Table::resetTable()`,允许同一 `Table` 在运行过程中动态切换表头与数据,并触发必要的单元格尺寸/分页信息重算与重绘。
- **TextBox 新增密码模式:**`TextBoxmode` 新增 `PASSWORD_MODE`;输入内容内部保存,绘制层面使用掩码字符(如 `*`)替代显示,真实文本可通过 `TextBox::getText()` 获取。
### ⚙️ 变更
- **TabControl 默认激活页语义明确化:**
- 首次绘制前调用 `TabControl::setActiveIndex()`:仅记录默认激活索引,不再立即触发页签按钮点击回调;
- 首次绘制完成后:若设置了默认激活索引则应用激活状态并绘制激活页(索引越界时默认激活最后一个页);
- 程序运行过程中调用 `TabControl::setActiveIndex()`:索引合法则立即切换激活页并绘制;索引越界则不做处理。
### ✅ 修复
- **TabControl::setActiveIndex 绘制前调用导致程序中断:**修复绘制前设置默认激活索引时触发空指针访问的问题;现在默认激活逻辑延后到首次绘制完成后再生效,避免崩溃并保证首次绘制即可绘制激活页。
- **TabControl 由不可见设置为可见时绘制错乱:**修复 `setIsVisible(false) -> setIsVisible(true)` 后非激活页被错误绘制导致的多页重叠/残影;现在 TabControl 可见时仅激活页可见/可绘制,无激活页则不绘制任何页。
## [v2.3.1] - 2025 - 11 - 30
### 🙏 鸣谢
- 感谢用户 [@To-KongBai](https://github.com/To-KongBai) 提供稳定复现步骤与关键现象对比(容器嵌套孙控件坐标转换问题),帮助我们快速确认多容器嵌套时的控件坐标转换问题并修复。([Issues#6](https://github.com/Ysm-04/StellarX/issues/6)
- 在即将上线的官网中ICP备案中我们计划加入一个贡献者鸣谢墙欢迎各位用户反馈BUG或者分享自己用星垣做的界面我们将认真阅读并收录鸣谢
- 真诚的感谢每一位反馈BUG的用户你们的反馈将使星垣更加稳定和健壮
### ✨ 新增
新增一个登录界面Demo在主仓库**examples/**目录下
### ⚙️ 变更
- **Dialog背景快照机制**`Dialog`不在自己抓取和销毁快照,**删除**重载的抓取和恢复快照的方法,完全交由基类`Canvas`处理,`Dialog``draw`方法中不在处理快照
- **窗口变化重绘时控件和窗口重绘的时机调整:**主事件循环中窗口大小发生变化时先处理控件尺寸,并回贴和释放旧快照,然后再重绘新尺寸窗口,最后绘制控件
### ✅ 修复
- **容器嵌套时子控件坐标转化:**`Canvas`重写了基类的`setX/Y`方法,在容器全局坐标发生变化时同步修改子控件的全局坐标,防止在容器嵌套时,容器的相对坐标被子控件当成全局坐标处理
- **纯色背景窗口标题不生效:**在`Window``draw()`方法中强制设置窗口标题,以防止,创建窗口时传递的窗口标题不生效
- **选项卡控件页签打开时动态改变坐标背景残留:**在`TabControl`重写的`setX/Y`方法中,当选项卡的坐标发生变化时强制让所有页签以及页和子控件丢一次快照,防止,在修改坐标后因,快照恢复顺序引起的选项卡激活页残留
- **Table动态改变坐标页码标签和翻页按钮错乱**在`Table`控件的`setX/Y`中重置`isNeedButtonAndPageNum`状态为真,在绘制时重新计算翻页按钮和页码标签的位置以保持在表格正下方居中位置显示
## [v2.3.0] - 2025 - 11 - 18 ## [v2.3.0] - 2025 - 11 - 18
### ✨ 新增 ### ✨ 新增

View File

@@ -7,8 +7,8 @@
![GitHub all releases](https://img.shields.io/github/downloads/Ysm-04/StellarX/total) ![GitHub all releases](https://img.shields.io/github/downloads/Ysm-04/StellarX/total)
[![Star GitHub Repo](https://img.shields.io/github/stars/Ysm-04/StellarX.svg?style=social&label=Star%20This%20Repo)](https://github.com/Ysm-04/StellarX) [![Star GitHub Repo](https://img.shields.io/github/stars/Ysm-04/StellarX.svg?style=social&label=Star%20This%20Repo)](https://github.com/Ysm-04/StellarX)
![Version](https://img.shields.io/badge/Version-2.3.0-brightgreen.svg) ![Version](https://img.shields.io/badge/Version-2.3.2-brightgreen.svg)
![Download](https://img.shields.io/badge/Download-2.3.0_Release-blue.svg) ![Download](https://img.shields.io/badge/Download-2.3.2_Release-blue.svg)
![C++](https://img.shields.io/badge/C++-17+-00599C?logo=cplusplus&logoColor=white) ![C++](https://img.shields.io/badge/C++-17+-00599C?logo=cplusplus&logoColor=white)
![Windows](https://img.shields.io/badge/Platform-Windows-0078D6?logo=windows) ![Windows](https://img.shields.io/badge/Platform-Windows-0078D6?logo=windows)
@@ -25,30 +25,6 @@ This is a **teaching-grade and tooling-grade** framework that helps developers u
------ ------
## 🆕 V2.3.0 - Major Update
**This version represents a significant milestone, introducing a responsive layout system that transitions from static to dynamic layout management, and comprehensively resolves the previously encountered random rendering corruption issues caused by reentrant drawing operations.**
- **Optimized Window Resizing Mechanism**: Refactored `WndProcThunk`, `runEventLoop`, and `pumpResizeIfNeeded` to uniformly record size changes and perform centralized repainting at the end of the event loop, eliminating jitter and sequencing confusion caused by repeated redraws.
- **New Dialog Size Scheduling Interface**: Introduced the combination of `Window::scheduleResizeFromModal()` and `pumpResizeIfNeeded()`, enabling modal dialogs to notify the parent window of size updates even during resizing operations. Underlying controls are relayout during unified finalization while dialogs maintain their original dimensions.
- **Enhanced Adaptive Layout System**: Internally added the `adaptiveLayout()` function to recalculate control positions and sizes based on anchor points, allowing dual-anchored controls (left-right or top-bottom) to adaptively stretch with window resizing.
- **Fixed Modal Dialog Resizing Issues**: Resolved the problem where window resizing while modal dialogs were open prevented underlying controls from updating their positions and sizes according to anchor points; simultaneously eliminated ghosting artifacts caused by repeated dialog redraws.
- **Further Resolved Drawing Sequence Confusion**: Replaced `InvalidateRect` with `ValidateRect` during resizing operations, ensuring the window is marked as valid only after a single unified drawing pass, preventing system-triggered `WM_PAINT` messages from causing reentrancy.
- **Additional Fixes**: Corrected delayed background snapshot updates in tables and dialogs under certain edge cases.
![](image/1.png)
![](image/2.png)
For details, please refer to the [CHANGELOG.en](CHANGELOG.en.md).
------
## 📦 Project Structure & Design Philosophy ## 📦 Project Structure & Design Philosophy
StellarX adopts classic **OOP** and **modular** design with a clear structure: StellarX adopts classic **OOP** and **modular** design with a clear structure:

View File

@@ -2,6 +2,8 @@
[English document](README.en.md) [English document](README.en.md)
官网地址https://stellarx-gui.top
> 本仓库为 **StellarX** 主仓:开发与 Issue/PR 均在 GitHub 进行。 > 本仓库为 **StellarX** 主仓:开发与 Issue/PR 均在 GitHub 进行。
> GitCode 仅为只读镜像:如需反馈请到 GitHub:https://github.com/Ysm-04/StellarX > GitCode 仅为只读镜像:如需反馈请到 GitHub:https://github.com/Ysm-04/StellarX
@@ -12,8 +14,8 @@
![GitHub all releases](https://img.shields.io/github/downloads/Ysm-04/StellarX/total) ![GitHub all releases](https://img.shields.io/github/downloads/Ysm-04/StellarX/total)
[![Star GitHub Repo](https://img.shields.io/github/stars/Ysm-04/StellarX.svg?style=social&label=Star%20This%20Repo)](https://github.com/Ysm-04/StellarX) [![Star GitHub Repo](https://img.shields.io/github/stars/Ysm-04/StellarX.svg?style=social&label=Star%20This%20Repo)](https://github.com/Ysm-04/StellarX)
![Version](https://img.shields.io/badge/Version-2.3.0-brightgreen.svg) ![Version](https://img.shields.io/badge/Version-2.3.2-brightgreen.svg)
![Download](https://img.shields.io/badge/Download-2.3.0_Release-blue.svg) ![Download](https://img.shields.io/badge/Download-2.3.2_Release-blue.svg)
![C++](https://img.shields.io/badge/C++-17+-00599C?logo=cplusplus&logoColor=white) ![C++](https://img.shields.io/badge/C++-17+-00599C?logo=cplusplus&logoColor=white)
![Windows](https://img.shields.io/badge/Platform-Windows-0078D6?logo=windows) ![Windows](https://img.shields.io/badge/Platform-Windows-0078D6?logo=windows)
@@ -30,27 +32,26 @@
--- ---
## 🆕V2.3.0——重要更新
**本版本是一次重大更新,增加了响应式布局系统,由静态布局转变为动态布局,并且彻底修复了之前存在的由重入重绘导致的概率出现的渲染错乱问题**
- **优化窗口尺寸调节机制**:重构 `WndProcThunk``runEventLoop``pumpResizeIfNeeded`,统一记录尺寸变化并在事件循环末尾集中重绘,避免重复重绘导致的抖动和顺序错乱。 ## 🆕V2.3.2——重要更新
- **新增对话框尺寸调度接口**:引入 `Window::scheduleResizeFromModal()``pumpResizeIfNeeded()` 的组合,模态对话框在拉伸期间也可通知父窗口更新尺寸。底层控件将在统一收口时重新布局,而对话框自身保持尺寸不变。 ### 新增
- **自适应布局改进**:内部新增 `adaptiveLayout()` 函数,按照锚点重新计算控件位置和尺寸,使双锚定(左右或上下)控件随窗口变化自适应伸缩 - **Table 支持运行期重置表头与数据:**新增 `Table::clearHeaders()``Table::clearData()``Table::resetTable()`,允许同一 `Table` 在运行过程中动态切换表头与数据,并触发必要的单元格尺寸/分页信息重算与重绘
- **TextBox 新增密码模式:**`TextBoxmode` 新增 `PASSWORD_MODE`;输入内容内部保存,绘制层面使用掩码字符(如 `*`)替代显示,真实文本可通过 `TextBox::getText()` 获取。
- **修复模态对话框拉伸问题**:解决模态对话框打开时,窗口拉伸导致底层控件无法根据锚点更新的位置和尺寸的问题;同时避免对话框反复重绘导致的残影。 ### ⚙️ 变更
- **进一步解决绘制顺序错乱**:拉伸过程中采用 `ValidateRect` 替代 `InvalidateRect`,确保窗口仅在一次统一收口绘制后标记为有效,杜绝系统再次触发 `WM_PAINT` 造成重入。 - **TabControl 默认激活页语义明确化:**
- 首次绘制前调用 `TabControl::setActiveIndex()`:仅记录默认激活索引,不再立即触发页签按钮点击回调;
- 首次绘制完成后:若设置了默认激活索引则应用激活状态并绘制激活页(索引越界时默认激活最后一个页);
- 程序运行过程中调用 `TabControl::setActiveIndex()`:索引合法则立即切换激活页并绘制;索引越界则不做处理。
- 其他修复:修正表格和对话框背景快照某些边界情况下的更新不及时问题。 ### ✅ 修复
![](image/1.png) - **TabControl::setActiveIndex 绘制前调用导致程序中断:**修复绘制前设置默认激活索引时触发空指针访问的问题;现在默认激活逻辑延后到首次绘制完成后再生效,避免崩溃并保证首次绘制即可绘制激活页。
- **TabControl 由不可见设置为可见时绘制错乱:**修复 `setIsVisible(false) -> setIsVisible(true)` 后非激活页被错误绘制导致的多页重叠/残影;现在 TabControl 可见时仅激活页可见/可绘制,无激活页则不绘制任何页。
![](image/2.png)
详细变更请参阅[更新日志](CHANGELOG.md)
--- ---

View File

@@ -24,7 +24,7 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
// 3. 为按钮设置点击事件使用Lambda表达式 // 3. 为按钮设置点击事件使用Lambda表达式
myButton->setOnClickListener([&mainWindow]() { myButton->setOnClickListener([&mainWindow]() {
// 使用消息框工厂创建模态对话框 // 使用消息框工厂创建模态对话框
auto result = StellarX::MessageBox::ShowModal( auto result = StellarX::MessageBox::showModal(
mainWindow, mainWindow,
"欢迎使用星垣GUI\r\n作者:我在人间做废物", "欢迎使用星垣GUI\r\n作者:我在人间做废物",
"问候", "问候",

View File

@@ -0,0 +1,127 @@
// 本Demo基于 StellarX 构建,轻量级的 Windows GUI 框架。
#include"StellarX.h"
int main()
{
Window win(1300, 800, NULL, RGB(255, 255, 0), "记账管理系统");
/*********登录界面***********/
//标签
std::unique_ptr<Label> logIn_label[3];
Label* p[3];
logIn_label[0] = std::make_unique<Label>(90, 150, "欢迎使用餐馆记账管理系统");
logIn_label[1] = std::make_unique<Label>(400, 300, "账号");
logIn_label[2] = std::make_unique<Label>(400, 400, "密码");
p[0] = logIn_label[0].get();
for (auto& log : logIn_label)
{
log->setTextdisap(true);
log->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
log->textStyle.lpszFace = "华文行楷";
}
logIn_label[0]->textStyle.nHeight = 100;
logIn_label[0]->setAnchor(StellarX::Anchor::Top, StellarX::Anchor::NoAnchor);
logIn_label[1]->textStyle.nHeight = 50;
logIn_label[1]->setAnchor(StellarX::Anchor::Bottom, StellarX::Anchor::NoAnchor);
logIn_label[2]->textStyle.nHeight = 50;
logIn_label[2]->setAnchor(StellarX::Anchor::Bottom, StellarX::Anchor::NoAnchor);
//输入框
std::unique_ptr<TextBox> logIn_textBox[2];
TextBox* logIn_textBox_ptr[2];
logIn_textBox[0] = std::make_unique<TextBox>(500, 295, 450, 50);
logIn_textBox[1] = std::make_unique<TextBox>(500, 395, 450, 50);
logIn_textBox_ptr[0] = logIn_textBox[0].get();
logIn_textBox_ptr[1] = logIn_textBox[1].get();
for (auto& tb : logIn_textBox)
{
tb->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
tb->setAnchor(StellarX::Anchor::Bottom, StellarX::Anchor::NoAnchor);
tb->setMaxCharLen(15);
}
//按钮
std::unique_ptr<Button> logIn_Button[2];
Button* logIn_Button_ptr[2];
logIn_Button[0] = std::make_unique<Button>(350, 500, 300, 50, "管理员登录");
logIn_Button[1] = std::make_unique<Button>(750, 500, 300, 50, "操作员登录");
logIn_Button_ptr[0] = logIn_Button[0].get();
logIn_Button_ptr[1] = logIn_Button[1].get();
for (auto& b : logIn_Button)
{
b->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
b->setAnchor(StellarX::Anchor::Bottom, StellarX::Anchor::NoAnchor);
}
//log画布
auto log_Canvas = std::make_unique<Canvas>(0, 0, 1300, 800);
Canvas* log_Canvas_ptr = log_Canvas.get();
log_Canvas_ptr->setCanvasfillMode(StellarX::FillMode::Null);
log_Canvas_ptr->setShape(StellarX::ControlShape::B_RECTANGLE);
log_Canvas_ptr->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
log_Canvas_ptr->setAnchor(StellarX::Anchor::Bottom, StellarX::Anchor::Top);
//将log界面控价加入logCanvas统一管理
for (auto& b : logIn_Button)
log_Canvas_ptr->addControl(std::move(b));
for (auto& tb : logIn_textBox)
log_Canvas_ptr->addControl(std::move(tb));
for (auto& la : logIn_label)
log_Canvas_ptr->addControl(std::move(la));
/**************业务UI****************/
auto tabControl = std::make_unique<TabControl>(10, 10, 1280, 780);
auto tabControl_ptr = tabControl.get();
tabControl_ptr->setIsVisible(false);
tabControl_ptr->setCanvasfillMode(StellarX::FillMode::Null);
tabControl_ptr->setShape(StellarX::ControlShape::ROUND_RECTANGLE);
tabControl_ptr->setTabPlacement(StellarX::TabPlacement::Left);
tabControl_ptr->setTabBarHeight(100);
//添加页签及页
auto tabP = std::make_pair(std::make_unique<Button>(0, 0, 100, 100, "点餐"), std::make_unique<Canvas>(0, 0, 1290, 790));
tabP.second->setCanvasfillMode(StellarX::FillMode::Null);
tabP.second->setShape(StellarX::ControlShape::ROUND_RECTANGLE);
tabControl_ptr->add(std::move(tabP));
/*------------login事件-------------*/
//管理员登录按钮事件
logIn_Button_ptr[0]->setOnClickListener([&p, &tabControl_ptr, &log_Canvas_ptr, &logIn_textBox_ptr, &win]()
{
if ("\0" == logIn_textBox_ptr[0]->getText() || "\0" == logIn_textBox_ptr[1]->getText())
{
if ("\0" == logIn_textBox_ptr[0]->getText())logIn_textBox_ptr[0]->setTextBoxBk(RGB(255, 0, 0));
if ("\0" == logIn_textBox_ptr[1]->getText())logIn_textBox_ptr[1]->setTextBoxBk(RGB(255, 0, 0));
std::cout << "\a";
StellarX::MessageBox::showModal(win, "账号或密码不能为空!", "提示");
}
else
{
log_Canvas_ptr->setIsVisible(false);
tabControl_ptr->setIsVisible(true);
win.draw("image\\bk1.jpg");
}
});
//操作员登录按钮事件
logIn_Button_ptr[1]->setOnClickListener([&tabControl_ptr, &log_Canvas_ptr, &logIn_textBox_ptr, &win]()
{
if ("\0" == logIn_textBox_ptr[0]->getText() || "\0" == logIn_textBox_ptr[1]->getText())
{
if ("\0" == logIn_textBox_ptr[0]->getText())logIn_textBox_ptr[0]->setTextBoxBk(RGB(255, 0, 0));
if ("\0" == logIn_textBox_ptr[1]->getText())logIn_textBox_ptr[1]->setTextBoxBk(RGB(255, 0, 0));
std::cout << "\a";
StellarX::MessageBox::showModal(win, "账号或密码不能为空!", "提示");
}
else
{
log_Canvas_ptr->setIsVisible(false);
tabControl_ptr->setIsVisible(true);
win.draw("image\\bk1.jpg");
}
});
win.addControl(std::move(log_Canvas));
win.addControl(std::move(tabControl));
win.draw("image\\bk1.jpg");
return win.runEventLoop();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -21,14 +21,14 @@
#include "Control.h" #include "Control.h"
#include"label.h" #include"label.h"
#define DISABLEDCOLOUR RGB(96, 96, 96) //禁用状态颜色 #define DISABLEDCOLOUR RGB(96, 96, 96) //禁用状态颜色
#define TEXTMARGINS_X 6 #define TEXTMARGINS_X 6
#define TEXTMARGINS_Y 4 #define TEXTMARGINS_Y 4
constexpr int bordWith = 1; //边框宽度,用于快照恢复时的偏移计算
constexpr int bordHeight = 1; //边框高度,用于快照恢复时的偏移计算
class Button : public Control class Button : public Control
{ {
std::string text; // 按钮上的文字 std::string text; // 按钮上的文字
bool click; // 是否被点击 bool click; // 是否被点击
bool hover; // 是否被悬停 bool hover; // 是否被悬停
@@ -42,7 +42,7 @@ class Button : public Control
COLORREF buttonTrueColor; // 按钮被点击后的颜色 COLORREF buttonTrueColor; // 按钮被点击后的颜色
COLORREF buttonFalseColor; // 按钮未被点击的颜色 COLORREF buttonFalseColor; // 按钮未被点击的颜色
COLORREF buttonHoverColor; // 按钮被鼠标悬停的颜色 COLORREF buttonHoverColor; // 按钮被鼠标悬停的颜色
COLORREF buttonBorderColor = RGB(0,0,0);// 按钮边框颜色 COLORREF buttonBorderColor = RGB(0, 0, 0);// 按钮边框颜色
StellarX::ButtonMode mode; // 按钮模式 StellarX::ButtonMode mode; // 按钮模式
StellarX::ControlShape shape; // 按钮形状 StellarX::ControlShape shape; // 按钮形状
@@ -51,8 +51,6 @@ class Button : public Control
StellarX::FillStyle buttonFillIma = StellarX::FillStyle::BDiagonal; //按钮填充图案 StellarX::FillStyle buttonFillIma = StellarX::FillStyle::BDiagonal; //按钮填充图案
IMAGE* buttonFileIMAGE = nullptr; //按钮填充图像 IMAGE* buttonFileIMAGE = nullptr; //按钮填充图像
std::function<void()> onClickCallback; //回调函数 std::function<void()> onClickCallback; //回调函数
std::function<void()> onToggleOnCallback; //TOGGLE模式下的回调函数 std::function<void()> onToggleOnCallback; //TOGGLE模式下的回调函数
std::function<void()> onToggleOffCallback; //TOGGLE模式下的回调函数 std::function<void()> onToggleOffCallback; //TOGGLE模式下的回调函数
@@ -94,7 +92,7 @@ public:
StellarX::ControlShape shape = StellarX::ControlShape::RECTANGLE); StellarX::ControlShape shape = StellarX::ControlShape::RECTANGLE);
//自定义按钮颜色和悬停颜色 //自定义按钮颜色和悬停颜色
Button(int x, int y, int width, int height, const std::string text, Button(int x, int y, int width, int height, const std::string text,
COLORREF ct, COLORREF cf,COLORREF ch, COLORREF ct, COLORREF cf, COLORREF ch,
StellarX::ButtonMode mode = StellarX::ButtonMode::NORMAL, StellarX::ControlShape shape = StellarX::ControlShape::RECTANGLE); StellarX::ButtonMode mode = StellarX::ButtonMode::NORMAL, StellarX::ControlShape shape = StellarX::ControlShape::RECTANGLE);
//析构函数 释放图形指针内存 //析构函数 释放图形指针内存
~Button(); ~Button();
@@ -173,7 +171,7 @@ public:
//设置提示框样式 //设置提示框样式
void setTooltipStyle(COLORREF text, COLORREF bk, bool transparent); void setTooltipStyle(COLORREF text, COLORREF bk, bool transparent);
//设置提示框文本 //设置提示框文本
void setTooltipText(const std::string& s){ tipTextClick = s; tipUserOverride = true; } void setTooltipText(const std::string& s) { tipTextClick = s; tipUserOverride = true; }
void setTooltipTextsForToggle(const std::string& onText, const std::string& offText); void setTooltipTextsForToggle(const std::string& onText, const std::string& offText);
private: private:
//初始化按钮 //初始化按钮
@@ -189,6 +187,4 @@ private:
void hideTooltip(); void hideTooltip();
// 根据当前 click 状态选择文案 // 根据当前 click 状态选择文案
void refreshTooltipTextForState(); void refreshTooltipTextForState();
}; };

View File

@@ -31,8 +31,7 @@ protected:
int canvaslinewidth = 1; //线宽 int canvaslinewidth = 1; //线宽
COLORREF canvasBorderClor = RGB(0, 0, 0); //边框颜色 COLORREF canvasBorderClor = RGB(0, 0, 0); //边框颜色
COLORREF canvasBkClor = RGB(255,255,255); //背景颜色 COLORREF canvasBkClor = RGB(255, 255, 255); //背景颜色
// 清除所有子控件 // 清除所有子控件
void clearAllControls(); void clearAllControls();
@@ -41,6 +40,9 @@ public:
Canvas(int x, int y, int width, int height); Canvas(int x, int y, int width, int height);
~Canvas() {} ~Canvas() {}
void setX(int x)override;
void setY(int y)override;
//绘制容器及其子控件 //绘制容器及其子控件
void draw() override; void draw() override;
bool handleEvent(const ExMessage& msg) override; bool handleEvent(const ExMessage& msg) override;
@@ -69,4 +71,3 @@ private:
//用来检查对话框是否模态,此控件不做实现 //用来检查对话框是否模态,此控件不做实现
bool model() const override { return false; }; bool model() const override { return false; };
}; };

View File

@@ -36,8 +36,8 @@ class Control
{ {
protected: protected:
std::string id; // 控件ID std::string id; // 控件ID
int localx,x, localy,y; // 左上角坐标 int localx, x, localy, y; // 左上角坐标
int localWidth,width, localHeight,height; // 控件尺寸 int localWidth, width, localHeight, height; // 控件尺寸
Control* parent = nullptr; // 父控件 Control* parent = nullptr; // 父控件
bool dirty = true; // 是否重绘 bool dirty = true; // 是否重绘
bool show = true; // 是否显示 bool show = true; // 是否显示
@@ -45,7 +45,7 @@ protected:
/* == 布局模式 == */ /* == 布局模式 == */
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式 StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
StellarX::Anchor anchor_1 = StellarX::Anchor::Top; // 锚点 StellarX::Anchor anchor_1 = StellarX::Anchor::Top; // 锚点
StellarX::Anchor anchor_2 = StellarX::Anchor::Left; // 锚点 StellarX::Anchor anchor_2 = StellarX::Anchor::Right; // 锚点
/* == 背景快照 == */ /* == 背景快照 == */
IMAGE* saveBkImage = nullptr; IMAGE* saveBkImage = nullptr;
@@ -66,9 +66,10 @@ protected:
Control(Control&&) = delete; Control(Control&&) = delete;
Control& operator=(Control&&) = delete; Control& operator=(Control&&) = delete;
Control() : localx(0),x(0), localy(0),y(0), localWidth(100),width(100),height(100), localHeight(100) {} Control() : localx(0), x(0), localy(0), y(0), localWidth(100), width(100), height(100), localHeight(100) {}
Control(int x, int y, int width, int height) Control(int x, int y, int width, int height)
: localx(x), x(x), localy(y), y(y), localWidth(width), width(width), height(height), localHeight(height){} : localx(x), x(x), localy(y), y(y), localWidth(width), width(width), height(height), localHeight(height) {
}
public: public:
virtual ~Control() virtual ~Control()
@@ -118,8 +119,8 @@ public:
int getLocalRight() const { return localx + localWidth; } int getLocalRight() const { return localx + localWidth; }
int getLocalBottom() const { return localy + localHeight; } int getLocalBottom() const { return localy + localHeight; }
void setX(int x) { this->x = x; dirty = true; } virtual void setX(int x) { this->x = x; dirty = true; }
void setY(int y) { this->y = y; dirty = true; } virtual void setY(int y) { this->y = y; dirty = true; }
virtual void setWidth(int width) { this->width = width; dirty = true; } virtual void setWidth(int width) { this->width = width; dirty = true; }
virtual void setHeight(int height) { this->height = height; dirty = true; } virtual void setHeight(int height) { this->height = height; dirty = true; }
public: public:
@@ -142,7 +143,7 @@ public:
virtual bool model()const = 0; virtual bool model()const = 0;
//布局 //布局
void setLayoutMode(StellarX::LayoutMode layoutMode_); void setLayoutMode(StellarX::LayoutMode layoutMode_);
void steAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2); void setAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2);
StellarX::Anchor getAnchor_1() const; StellarX::Anchor getAnchor_1() const;
StellarX::Anchor getAnchor_2() const; StellarX::Anchor getAnchor_2() const;
StellarX::LayoutMode getLayoutMode() const; StellarX::LayoutMode getLayoutMode() const;

View File

@@ -207,7 +207,8 @@ namespace StellarX
enum class TextBoxmode enum class TextBoxmode
{ {
INPUT_MODE, // 用户可输入模式 INPUT_MODE, // 用户可输入模式
READONLY_MODE // 只读模式 READONLY_MODE, // 只读模式
PASSWORD_MODE// 密码模式
}; };
/** /**

View File

@@ -45,7 +45,6 @@ class Dialog : public Canvas
std::string message; //提示信息 std::string message; //提示信息
std::vector<std::string> lines; //消息内容按行分割 std::vector<std::string> lines; //消息内容按行分割
bool needsInitialization = true; //是否需要初始化 bool needsInitialization = true; //是否需要初始化
bool close = false; //是否关闭 bool close = false; //是否关闭
bool modal = true; //是否模态 bool modal = true; //是否模态
@@ -57,7 +56,6 @@ class Dialog : public Canvas
COLORREF buttonFalseColor = RGB(215, 215, 215); //按钮未被点击颜色 COLORREF buttonFalseColor = RGB(215, 215, 215); //按钮未被点击颜色
COLORREF buttonHoverColor = RGB(224, 224, 224); //按钮悬浮颜色 COLORREF buttonHoverColor = RGB(224, 224, 224); //按钮悬浮颜色
Button* closeButton = nullptr; //关闭按钮 Button* closeButton = nullptr; //关闭按钮
StellarX::MessageBoxResult result = StellarX::MessageBoxResult::Cancel; // 对话框结果 StellarX::MessageBoxResult result = StellarX::MessageBoxResult::Cancel; // 对话框结果
@@ -78,7 +76,6 @@ public:
//获取对话框消息,用以去重 //获取对话框消息,用以去重
std::string GetText() const; std::string GetText() const;
public: public:
Dialog(Window& hWnd, std::string text, std::string message = "对话框", StellarX::MessageBoxType type = StellarX::MessageBoxType::OK, bool modal = true); Dialog(Window& hWnd, std::string text, std::string message = "对话框", StellarX::MessageBoxType type = StellarX::MessageBoxType::OK, bool modal = true);
~Dialog(); ~Dialog();
@@ -109,7 +106,6 @@ public:
//初始化 //初始化
void setInitialization(bool init); void setInitialization(bool init);
private: private:
// 初始化按钮 // 初始化按钮
void initButtons(); void initButtons();
@@ -123,9 +119,6 @@ private:
void getTextSize(); void getTextSize();
//初始化对话框尺寸 //初始化对话框尺寸
void initDialogSize(); void initDialogSize();
void saveBackground(int x, int y, int w, int h)override;
void restBackground()override;
void addControl(std::unique_ptr<Control> control); void addControl(std::unique_ptr<Control> control);
// 清除所有控件 // 清除所有控件

View File

@@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* @文件: StellarX.h * @文件: StellarX.h
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件 * @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
* @版本: v2.3.0 * @版本: v2.3.2
* @描述: * @描述:
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。 * 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
* 基于EasyX图形库提供简洁易用的API和丰富的控件。 * 基于EasyX图形库提供简洁易用的API和丰富的控件。
@@ -11,6 +11,7 @@
* *
* @作者: 我在人间做废物 * @作者: 我在人间做废物
* @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com] * @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com]
* @官网https://stellarx-gui.top/
* @仓库: [https://github.com/Ysm-04/StellarX] * @仓库: [https://github.com/Ysm-04/StellarX]
* *
* @许可证: MIT License * @许可证: MIT License

View File

@@ -26,8 +26,10 @@
class TabControl :public Canvas class TabControl :public Canvas
{ {
int tabBarHeight = BUTMINWIDTH; //页签栏高度 int tabBarHeight = BUTMINWIDTH; //页签栏高度
StellarX::TabPlacement tabPlacement = StellarX::TabPlacement::Top ; //页签排列方式 bool IsFirstDraw = true; //首次绘制标记
std::vector<std::pair<std::unique_ptr<Button>,std::unique_ptr<Canvas>>> controls; //页签/页列表 int defaultActivation = -1; //默认激活页签索引
StellarX::TabPlacement tabPlacement = StellarX::TabPlacement::Top; //页签排列方式
std::vector<std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>> controls; //页签/页列表
private: private:
using Canvas::addControl; // 禁止外部误用 using Canvas::addControl; // 禁止外部误用
@@ -37,16 +39,19 @@ private:
inline void initTabPage(); inline void initTabPage();
public: public:
TabControl(); TabControl();
TabControl(int x,int y,int width,int height); TabControl(int x, int y, int width, int height);
~TabControl(); ~TabControl();
void setX(int x)override;
void setY(int y)override;
void draw() override; void draw() override;
bool handleEvent(const ExMessage& msg) override; bool handleEvent(const ExMessage& msg) override;
//添加页签+页 //添加页签+页
void add(std::pair<std::unique_ptr<Button> ,std::unique_ptr<Canvas>>&& control); void add(std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>&& control);
//添加为某个页添加控件 //添加为某个页添加控件
void add(std::string tabText,std::unique_ptr<Control> control); void add(std::string tabText, std::unique_ptr<Control> control);
//设置页签位置 //设置页签位置
void setTabPlacement(StellarX::TabPlacement placement); void setTabPlacement(StellarX::TabPlacement placement);
//设置页签栏高度 两侧排列时为宽度 //设置页签栏高度 两侧排列时为宽度
@@ -64,6 +69,4 @@ public:
int indexOf(const std::string& tabText) const; int indexOf(const std::string& tabText) const;
void setDirty(bool dirty) override; void setDirty(bool dirty) override;
void requestRepaint(Control* parent)override; void requestRepaint(Control* parent)override;
}; };

View File

@@ -33,7 +33,7 @@ public:
StellarX::ControlText textStyle; //标签文本样式 StellarX::ControlText textStyle; //标签文本样式
public: public:
Label(); Label();
Label(int x, int y, std::string text = "标签",COLORREF textcolor = BLACK, COLORREF bkColor= RGB(255,255,255)); Label(int x, int y, std::string text = "标签", COLORREF textcolor = BLACK, COLORREF bkColor = RGB(255, 255, 255));
void draw() override; void draw() override;
void hide(); void hide();
@@ -43,6 +43,4 @@ public:
void setTextBkColor(COLORREF color); void setTextBkColor(COLORREF color);
//设置标签文本 //设置标签文本
void setText(std::string text); void setText(std::string text);
}; };

View File

@@ -49,7 +49,6 @@
#define TABLE_STR_PAGE_MID "页/共" #define TABLE_STR_PAGE_MID "页/共"
#define TABLE_STR_PAGE_SUFFIX "页" #define TABLE_STR_PAGE_SUFFIX "页"
class Table :public Control class Table :public Control
{ {
private: private:
@@ -99,6 +98,8 @@ private:
bool model() const override { return false; }; bool model() const override { return false; };
public: public:
StellarX::ControlText textStyle; // 文本样式 StellarX::ControlText textStyle; // 文本样式
void setX(int x) override;
void setY(int y) override;
void setWidth(int width) override; void setWidth(int width) override;
void setHeight(int height) override; void setHeight(int height) override;
public: public:
@@ -129,6 +130,12 @@ public:
void setTableLineStyle(StellarX::LineStyle style); void setTableLineStyle(StellarX::LineStyle style);
//设置边框宽度 //设置边框宽度
void setTableBorderWidth(int width); void setTableBorderWidth(int width);
//清空表头
void clearHeaders();
//清空表格数据
void clearData();
//清空表头和数据
void resetTable();
//窗口变化丢快照+标脏 //窗口变化丢快照+标脏
void onWindowResize() override; void onWindowResize() override;
@@ -151,7 +158,7 @@ public:
//获取线型 //获取线型
StellarX::LineStyle getTableLineStyle() const; StellarX::LineStyle getTableLineStyle() const;
//获取表头 //获取表头
std::vector<std::string> getHeaders () const; std::vector<std::string> getHeaders() const;
//获取表格数据 //获取表格数据
std::vector<std::vector<std::string>> getData() const; std::vector<std::vector<std::string>> getData() const;
//获取表格边框宽度 //获取表格边框宽度
@@ -159,7 +166,4 @@ public:
//获取表格尺寸 //获取表格尺寸
int getTableWidth() const; int getTableWidth() const;
int getTableHeight() const; int getTableHeight() const;
}; };

View File

@@ -18,7 +18,6 @@
#pragma once #pragma once
#include "Control.h" #include "Control.h"
class TextBox : public Control class TextBox : public Control
{ {
std::string text; //文本 std::string text; //文本
@@ -27,7 +26,7 @@ class TextBox : public Control
bool click = false; //是否点击 bool click = false; //是否点击
size_t maxCharLen = 10;//最大字符长度 size_t maxCharLen = 10;//最大字符长度
COLORREF textBoxBkClor = RGB(255, 255, 255); //背景颜色 COLORREF textBoxBkClor = RGB(255, 255, 255); //背景颜色
COLORREF textBoxBorderClor = RGB(0,0,0); //边框颜色 COLORREF textBoxBorderClor = RGB(0, 0, 0); //边框颜色
public: public:
StellarX::ControlText textStyle; //文本样式 StellarX::ControlText textStyle; //文本样式
@@ -55,5 +54,3 @@ private:
//用来检查对话框是否模态,此控件不做实现 //用来检查对话框是否模态,此控件不做实现
bool model() const override { return false; }; bool model() const override { return false; };
}; };

View File

@@ -1,5 +1,4 @@
/**
/**
* Window头文件 * Window头文件
* *
* 设计目标: * 设计目标:
@@ -101,10 +100,9 @@ public:
useComposited = on; useComposited = on;
} }
void processWindowMessage(const ExMessage & msg); // 处理 EX_WINDOW 中的 WM_SIZE 等 void processWindowMessage(const ExMessage& msg); // 处理 EX_WINDOW 中的 WM_SIZE 等
void pumpResizeIfNeeded(); // 执行一次统一收口重绘 void pumpResizeIfNeeded(); // 执行一次统一收口重绘
void scheduleResizeFromModal(int w, int h); void scheduleResizeFromModal(int w, int h);
private: private:
void adaptiveLayout(std::unique_ptr<Control>& c,const int finalH, const int finalW); void adaptiveLayout(std::unique_ptr<Control>& c, const int finalH, const int finalW);
}; };

View File

@@ -163,7 +163,6 @@ void Button::initButton(const std::string text, StellarX::ButtonMode mode, Stell
tipLabel.textStyle = this->textStyle; // 复用按钮字体样式 tipLabel.textStyle = this->textStyle; // 复用按钮字体样式
} }
Button::~Button() Button::~Button()
{ {
if (buttonFileIMAGE) if (buttonFileIMAGE)
@@ -224,7 +223,7 @@ void Button::draw()
//设置按钮填充模式 //设置按钮填充模式
setfillstyle((int)buttonFillMode, (int)buttonFillIma, buttonFileIMAGE); setfillstyle((int)buttonFillMode, (int)buttonFillIma, buttonFileIMAGE);
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage) if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
saveBackground(this->x, this->y, this->width, this->height); saveBackground(this->x, this->y, (this->width + bordWith), (this->height + bordHeight));
// 恢复背景(清除旧内容) // 恢复背景(清除旧内容)
restBackground(); restBackground();
//根据按钮形状绘制 //根据按钮形状绘制
@@ -274,7 +273,6 @@ void Button::draw()
restoreStyle();//恢复默认字体样式和颜色 restoreStyle();//恢复默认字体样式和颜色
dirty = false; //标记按钮不需要重绘 dirty = false; //标记按钮不需要重绘
} }
// 处理鼠标事件,检测点击和悬停状态 // 处理鼠标事件,检测点击和悬停状态
// 根据按钮模式和形状进行不同的处理 // 根据按钮模式和形状进行不同的处理
@@ -314,7 +312,6 @@ bool Button::handleEvent(const ExMessage& msg)
// 处理鼠标点击事件 // 处理鼠标点击事件
if (msg.message == WM_LBUTTONDOWN && hover && mode != StellarX::ButtonMode::DISABLED) if (msg.message == WM_LBUTTONDOWN && hover && mode != StellarX::ButtonMode::DISABLED)
{ {
if (mode == StellarX::ButtonMode::NORMAL) if (mode == StellarX::ButtonMode::NORMAL)
{ {
click = true; click = true;
@@ -451,7 +448,6 @@ void Button::setROUND_RECTANGLEwidth(int width)
{ {
rouRectangleSize.ROUND_RECTANGLEwidth = width; rouRectangleSize.ROUND_RECTANGLEwidth = width;
this->dirty = true; // 标记需要重绘 this->dirty = true; // 标记需要重绘
} }
void Button::setROUND_RECTANGLEheight(int height) void Button::setROUND_RECTANGLEheight(int height)
@@ -489,7 +485,6 @@ void Button::setFillIma(std::string imaNAme)
this->dirty = true; this->dirty = true;
} }
void Button::setButtonBorder(COLORREF Border) void Button::setButtonBorder(COLORREF Border)
{ {
buttonBorderColor = Border; buttonBorderColor = Border;
@@ -558,7 +553,6 @@ void Button::setButtonClick(BOOL click)
requestRepaint(parent); requestRepaint(parent);
} }
std::string Button::getButtonText() const std::string Button::getButtonText() const
{ {
return this->text; return this->text;
@@ -619,8 +613,6 @@ int Button::getButtonHeight() const
return this->height; return this->height;
} }
bool Button::isMouseInCircle(int mouseX, int mouseY, int x, int y, int radius) bool Button::isMouseInCircle(int mouseX, int mouseY, int x, int y, int radius)
{ {
double dis = sqrt(pow(mouseX - x, 2) + pow(mouseY - y, 2)); double dis = sqrt(pow(mouseX - x, 2) + pow(mouseY - y, 2));
@@ -666,11 +658,9 @@ void Button::cutButtonText()
else else
{ {
cutText = ellipsize_cjk_pref(this->text, contentW, ""); // 全角省略号 cutText = ellipsize_cjk_pref(this->text, contentW, ""); // 全角省略号
} }
isUseCutText = true; isUseCutText = true;
needCutText = false; needCutText = false;
} }
void Button::hideTooltip() void Button::hideTooltip()
@@ -691,6 +681,3 @@ void Button::refreshTooltipTextForState()
else if (mode == StellarX::ButtonMode::TOGGLE) else if (mode == StellarX::ButtonMode::TOGGLE)
tipLabel.setText(click ? tipTextOn : tipTextOff); tipLabel.setText(click ? tipTextOn : tipTextOff);
} }

View File

@@ -12,6 +12,28 @@ Canvas::Canvas(int x, int y, int width, int height)
this->id = "Canvas"; this->id = "Canvas";
} }
void Canvas::setX(int x)
{
this->x = x;
for (auto& c : controls)
{
c->onWindowResize();
c->setX(c->getLocalX() + this->x);
}
dirty = true;
}
void Canvas::setY(int y)
{
this->y = y;
for (auto& c : controls)
{
c->onWindowResize();
c->setY(c->getLocalY() + this->y);
}
dirty = true;
}
void Canvas::clearAllControls() void Canvas::clearAllControls()
{ {
controls.clear(); controls.clear();
@@ -28,7 +50,7 @@ void Canvas::draw()
} }
saveStyle(); saveStyle();
setlinecolor(canvasBorderClor);//设置线色 setlinecolor(canvasBorderClor);//设置线色
if(StellarX::FillMode::Null != canvasFillMode) if (StellarX::FillMode::Null != canvasFillMode)
setfillcolor(canvasBkClor);//设置填充色 setfillcolor(canvasBkClor);//设置填充色
setfillstyle((int)canvasFillMode);//设置填充模式 setfillstyle((int)canvasFillMode);//设置填充模式
setlinestyle((int)canvasLineStyle, canvaslinewidth); setlinestyle((int)canvasLineStyle, canvaslinewidth);
@@ -36,21 +58,22 @@ void Canvas::draw()
// 在绘制画布之前,先恢复并更新背景快照: // 在绘制画布之前,先恢复并更新背景快照:
// 1. 如果已有快照,则先回贴旧快照以清除之前的内容。 // 1. 如果已有快照,则先回贴旧快照以清除之前的内容。
// 2. 当坐标或尺寸变化,或缓存图像无效时,丢弃旧快照并重新抓取新的背景。 // 2. 当坐标或尺寸变化,或缓存图像无效时,丢弃旧快照并重新抓取新的背景。
int margin = canvaslinewidth > 1 ? canvaslinewidth : 1;
if (hasSnap) if (hasSnap)
{ {
// 恢复旧快照,清除上一次绘制 // 恢复旧快照,清除上一次绘制
restBackground(); restBackground();
// 如果位置或尺寸变了,或没有有效缓存,则重新抓取 // 如果位置或尺寸变了,或没有有效缓存,则重新抓取
if (!saveBkImage || saveBkX != this->x || saveBkY != this->y || saveWidth != this->width || saveHeight != this->height) if (!saveBkImage || saveBkX != this->x - margin || saveBkY != this->y - margin || saveWidth != this->width + margin * 2 || saveHeight != this->height + margin * 2)
{ {
discardBackground(); discardBackground();
saveBackground(this->x, this->y, this->width, this->height); saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
} }
} }
else else
{ {
// 首次绘制或没有快照时直接抓取背景 // 首次绘制或没有快照时直接抓取背景
saveBackground(this->x, this->y, this->width, this->height); saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
} }
// 再次恢复最新快照,确保绘制区域干净 // 再次恢复最新快照,确保绘制区域干净
restBackground(); restBackground();
@@ -58,7 +81,7 @@ void Canvas::draw()
switch (shape) switch (shape)
{ {
case StellarX::ControlShape::RECTANGLE: case StellarX::ControlShape::RECTANGLE:
fillrectangle(x,y,x+width,y+height);//有边框填充矩形 fillrectangle(x, y, x + width, y + height);//有边框填充矩形
break; break;
case StellarX::ControlShape::B_RECTANGLE: case StellarX::ControlShape::B_RECTANGLE:
solidrectangle(x, y, x + width, y + height);//无边框填充矩形 solidrectangle(x, y, x + width, y + height);//无边框填充矩形
@@ -151,7 +174,6 @@ void Canvas::setCanvasLineStyle(StellarX::LineStyle style)
dirty = true; dirty = true;
} }
void Canvas::setLinewidth(int width) void Canvas::setLinewidth(int width)
{ {
this->canvaslinewidth = width; this->canvaslinewidth = width;
@@ -174,7 +196,7 @@ void Canvas::setIsVisible(bool visible)
void Canvas::setDirty(bool dirty) void Canvas::setDirty(bool dirty)
{ {
this->dirty = dirty; this->dirty = dirty;
for(auto& control : controls) for (auto& control : controls)
control->setDirty(dirty); control->setDirty(dirty);
} }
@@ -360,6 +382,3 @@ void Canvas::requestRepaint(Control* parent)
else else
onRequestRepaintAsRoot(); onRequestRepaintAsRoot();
} }

View File

@@ -44,9 +44,14 @@ bool StellarX::ControlText::operator!=(const ControlText& text)
} }
void Control::setIsVisible(bool show) void Control::setIsVisible(bool show)
{ {
this->show = show;
dirty = true;
if (!show) if (!show)
this->updateBackground(); this->updateBackground();
this->show = show; else
requestRepaint(parent);
} }
void Control::onWindowResize() void Control::onWindowResize()
{ {
@@ -58,7 +63,7 @@ void Control::setLayoutMode(StellarX::LayoutMode layoutMode_)
{ {
this->layoutMode = layoutMode_; this->layoutMode = layoutMode_;
} }
void Control::steAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2) void Control::setAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2)
{ {
this->anchor_1 = anchor_1; this->anchor_1 = anchor_1;
this->anchor_2 = anchor_2; this->anchor_2 = anchor_2;
@@ -144,6 +149,7 @@ void Control::discardBackground()
{ {
if (saveBkImage) if (saveBkImage)
{ {
restBackground();
delete saveBkImage; delete saveBkImage;
saveBkImage = nullptr; saveBkImage = nullptr;
} }

View File

@@ -1,21 +1,19 @@
#include "Dialog.h" #include "Dialog.h"
Dialog::Dialog(Window& h,std::string text,std::string message, StellarX::MessageBoxType type, bool modal) Dialog::Dialog(Window& h, std::string text, std::string message, StellarX::MessageBoxType type, bool modal)
: Canvas(),message(message), type(type), modal(modal), hWnd(h), titleText(text) : Canvas(), message(message), type(type), modal(modal), hWnd(h), titleText(text)
{ {
this->id = "Dialog"; this->id = "Dialog";
show = false; show = false;
} }
Dialog::~Dialog() Dialog::~Dialog()
{ {
} }
void Dialog::draw() void Dialog::draw()
{ {
if(!show) if (!show)
{ {
// 如果对话框不可见且需要清理,执行清理 // 如果对话框不可见且需要清理,执行清理
if (pendingCleanup && !isCleaning) if (pendingCleanup && !isCleaning)
@@ -41,12 +39,6 @@ void Dialog::draw()
Canvas::setCanvasBkColor(this->backgroundColor); Canvas::setCanvasBkColor(this->backgroundColor);
Canvas::setShape(StellarX::ControlShape::ROUND_RECTANGLE); Canvas::setShape(StellarX::ControlShape::ROUND_RECTANGLE);
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
saveBackground(this->x, this->y, this->width, this->height);
//设置所有控件为脏状态
/*for(auto& c :this->controls)
c->setDirty(true);*/
restBackground();
Canvas::draw(); Canvas::draw();
//绘制消息文本 //绘制消息文本
@@ -57,9 +49,8 @@ void Dialog::draw()
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight, textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标 int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标
for (auto& line:lines) for (auto& line : lines)
{ {
int tx = this->x + ((this->width - textwidth(line.c_str())) / 2); // 文本起始X坐标 int tx = this->x + ((this->width - textwidth(line.c_str())) / 2); // 文本起始X坐标
outtextxy(tx, ty, LPCTSTR(line.c_str())); outtextxy(tx, ty, LPCTSTR(line.c_str()));
@@ -73,8 +64,6 @@ void Dialog::draw()
} }
} }
bool Dialog::handleEvent(const ExMessage& msg) bool Dialog::handleEvent(const ExMessage& msg)
{ {
bool consume = false; bool consume = false;
@@ -140,7 +129,6 @@ void Dialog::SetModal(bool modal)
this->modal = modal; this->modal = modal;
} }
void Dialog::SetResult(StellarX::MessageBoxResult result) void Dialog::SetResult(StellarX::MessageBoxResult result)
{ {
this->result = result; this->result = result;
@@ -244,8 +232,6 @@ void Dialog::Show()
dirty = true; dirty = true;
} }
void Dialog::Close() void Dialog::Close()
{ {
if (!show) return; if (!show) return;
@@ -255,12 +241,9 @@ void Dialog::Close()
dirty = true; dirty = true;
pendingCleanup = true; // 只标记需要清理,不立即执行 pendingCleanup = true; // 只标记需要清理,不立即执行
// 工厂模式下非模态触发回调 返回结果 // 工厂模式下非模态触发回调 返回结果
if (resultCallback&& !modal) if (resultCallback && !modal)
resultCallback(this->result); resultCallback(this->result);
} }
void Dialog::setInitialization(bool init) void Dialog::setInitialization(bool init)
@@ -269,10 +252,10 @@ void Dialog::setInitialization(bool init)
{ {
initDialogSize(); initDialogSize();
saveBackground((x - BorderWidth), (y - BorderWidth), (width + 2 * BorderWidth), (height + 2 * BorderWidth)); saveBackground((x - BorderWidth), (y - BorderWidth), (width + 2 * BorderWidth), (height + 2 * BorderWidth));
this->dirty = true;
} }
} }
void Dialog::initButtons() void Dialog::initButtons()
{ {
controls.clear(); controls.clear();
@@ -396,7 +379,6 @@ void Dialog::initButtons()
noButton->textStyle = this->textStyle; noButton->textStyle = this->textStyle;
cancelButton->textStyle = this->textStyle; cancelButton->textStyle = this->textStyle;
this->addControl(std::move(yesButton)); this->addControl(std::move(yesButton));
this->addControl(std::move(noButton)); this->addControl(std::move(noButton));
this->addControl(std::move(cancelButton)); this->addControl(std::move(cancelButton));
@@ -436,7 +418,7 @@ void Dialog::initButtons()
case StellarX::MessageBoxType::AbortRetryIgnore: // 中止、重试和忽略按钮 case StellarX::MessageBoxType::AbortRetryIgnore: // 中止、重试和忽略按钮
{ {
auto abortButton = createDialogButton( auto abortButton = createDialogButton(
(this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin* (buttonNum-1))) / 2), (this->x + (this->width - (functionButtonWidth * buttonNum + buttonMargin * (buttonNum - 1))) / 2),
((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2), ((this->y + (this->height - buttonAreaHeight)) + (buttonAreaHeight - functionButtonHeight) / 2),
"中止" "中止"
); );
@@ -486,7 +468,7 @@ void Dialog::initCloseButton()
//初始化关闭按钮 //初始化关闭按钮
auto but = std::make_unique<Button> auto but = std::make_unique<Button>
( (
(this->x + this->width - closeButtonWidth) - 3, (this->y+3), closeButtonWidth-1, closeButtonHeight, (this->x + this->width - closeButtonWidth) - 3, (this->y + 3), closeButtonWidth - 1, closeButtonHeight,
"X", // 按钮文本 "X", // 按钮文本
RGB(255, 0, 0), // 按钮被点击颜色 RGB(255, 0, 0), // 按钮被点击颜色
this->canvasBkClor, // 按钮背景颜色 this->canvasBkClor, // 按钮背景颜色
@@ -507,7 +489,7 @@ void Dialog::initCloseButton()
void Dialog::initTitle() void Dialog::initTitle()
{ {
this->title = std::make_unique<Label>(this->x+5,this->y+5,titleText,textStyle.color); this->title = std::make_unique<Label>(this->x + 5, this->y + 5, titleText, textStyle.color);
title->setTextdisap(true); title->setTextdisap(true);
title->textStyle = this->textStyle; title->textStyle = this->textStyle;
@@ -521,7 +503,7 @@ void Dialog::splitMessageLines()
std::string currentLine; std::string currentLine;
for (size_t i = 0; i < message.length(); i++) { for (size_t i = 0; i < message.length(); i++) {
// 处理 换行符 \r\n \n \r // 处理 换行符 \r\n \n \r
if (i + 1 < message.length() && (message[i] == '\r' || message[i] == '\n')||(message[i] == '\r' && message[i+1] == '\n')) if (i + 1 < message.length() && (message[i] == '\r' || message[i] == '\n') || (message[i] == '\r' && message[i + 1] == '\n'))
{ {
if (!currentLine.empty()) { if (!currentLine.empty()) {
lines.push_back(currentLine); lines.push_back(currentLine);
@@ -592,10 +574,10 @@ void Dialog::initDialogSize()
// 计算按钮区域宽度 // 计算按钮区域宽度
int buttonAreaWidth = buttonNum * functionButtonWidth + int buttonAreaWidth = buttonNum * functionButtonWidth +
(buttonNum > 0 ? (buttonNum +1) * buttonMargin : 0); (buttonNum > 0 ? (buttonNum + 1) * buttonMargin : 0);
// 计算文本区域宽度(包括边距) // 计算文本区域宽度(包括边距)
int textAreaWidth = textWidth + textToBorderMargin * 2 ; int textAreaWidth = textWidth + textToBorderMargin * 2;
// 对话框宽度取两者中的较大值,并确保最小宽度 // 对话框宽度取两者中的较大值,并确保最小宽度
this->width = buttonAreaWidth > textAreaWidth ? buttonAreaWidth : textAreaWidth; this->width = buttonAreaWidth > textAreaWidth ? buttonAreaWidth : textAreaWidth;
@@ -603,7 +585,7 @@ void Dialog::initDialogSize()
// 计算对话框高度 // 计算对话框高度
// 高度 = 标题栏高度 + 文本区域高度 + 按钮区域高度 + 间距 // 高度 = 标题栏高度 + 文本区域高度 + 按钮区域高度 + 间距
int textAreaHeight = textHeight * (int)lines.size() + 5*((int)lines.size()-1); // 文本行高+行间距 int textAreaHeight = textHeight * (int)lines.size() + 5 * ((int)lines.size() - 1); // 文本行高+行间距
this->height = closeButtonHeight + // 标题栏高度 this->height = closeButtonHeight + // 标题栏高度
titleToTextMargin + // 标题到文本的间距 titleToTextMargin + // 标题到文本的间距
textAreaHeight + // 文本区域高度 textAreaHeight + // 文本区域高度
@@ -621,32 +603,6 @@ void Dialog::initDialogSize()
initCloseButton(); // 初始化关闭按钮 initCloseButton(); // 初始化关闭按钮
} }
void Dialog::saveBackground(int x, int y, int w, int h)
{
if (w <= 0 || h <= 0) return;
saveBkX = x; saveBkY = y; saveWidth = w; saveHeight = h;
if (saveBkImage)
{
//尺寸变了才重建,避免反复 new/delete
if (saveBkImage->getwidth() != w || saveBkImage->getheight() != h)
{
delete saveBkImage; saveBkImage = nullptr;
}
}
if (!saveBkImage) saveBkImage = new IMAGE(w + BorderWidth*2, h + BorderWidth*2);
SetWorkingImage(nullptr); // ★抓屏幕
getimage(saveBkImage, x - BorderWidth, y - BorderWidth, w + BorderWidth*2, h+ BorderWidth*2);
hasSnap = true;
}
void Dialog::restBackground()
{
if (!hasSnap || !saveBkImage) return;
// 直接回贴屏幕(与抓取一致)
SetWorkingImage(nullptr);
putimage(saveBkX - BorderWidth, saveBkY - BorderWidth,saveBkImage);
}
void Dialog::addControl(std::unique_ptr<Control> control) void Dialog::addControl(std::unique_ptr<Control> control)
{ {
control->setParent(this); control->setParent(this);
@@ -748,7 +704,6 @@ void Dialog::requestRepaint(Control* parent)
for (auto& control : controls) for (auto& control : controls)
if (control->isDirty() && control->IsVisible()) if (control->isDirty() && control->IsVisible())
control->draw(); control->draw();
} }
else else
onRequestRepaintAsRoot(); onRequestRepaintAsRoot();

View File

@@ -2,7 +2,7 @@
namespace StellarX namespace StellarX
{ {
MessageBoxResult MessageBox::showModal(Window& wnd,const std::string& text,const std::string& caption, MessageBoxResult MessageBox::showModal(Window& wnd, const std::string& text, const std::string& caption,
MessageBoxType type) MessageBoxType type)
{ {
Dialog dlg(wnd, caption, text, type, true); // 模态 Dialog dlg(wnd, caption, text, type, true); // 模态
@@ -11,7 +11,7 @@ namespace StellarX
return dlg.GetResult(); return dlg.GetResult();
} }
void MessageBox::showAsync(Window& wnd,const std::string& text,const std::string& caption,MessageBoxType type, void MessageBox::showAsync(Window& wnd, const std::string& text, const std::string& caption, MessageBoxType type,
std::function<void(MessageBoxResult)> onResult) std::function<void(MessageBoxResult)> onResult)
{ {
//去重,如果窗口内已有相同的对话框被触发,则不再创建 //去重,如果窗口内已有相同的对话框被触发,则不再创建

View File

@@ -33,7 +33,7 @@ inline void TabControl::initTabBar()
for (auto& c : controls) for (auto& c : controls)
{ {
c.first->setX(this->x + i * butW); c.first->setX(this->x + i * butW);
c.first->setY(this->y+this->height - tabBarHeight); c.first->setY(this->y + this->height - tabBarHeight);
i++; i++;
} }
break; break;
@@ -41,14 +41,14 @@ inline void TabControl::initTabBar()
for (auto& c : controls) for (auto& c : controls)
{ {
c.first->setX(this->x); c.first->setX(this->x);
c.first->setY(this->y+i* butH); c.first->setY(this->y + i * butH);
i++; i++;
} }
break; break;
case StellarX::TabPlacement::Right: case StellarX::TabPlacement::Right:
for (auto& c : controls) for (auto& c : controls)
{ {
c.first->setX(this->x+this->width - tabBarHeight); c.first->setX(this->x + this->width - tabBarHeight);
c.first->setY(this->y + i * butH); c.first->setY(this->y + i * butH);
i++; i++;
} }
@@ -147,7 +147,7 @@ inline void TabControl::initTabPage()
} }
} }
TabControl::TabControl():Canvas() TabControl::TabControl() :Canvas()
{ {
this->id = "TabControl"; this->id = "TabControl";
} }
@@ -162,28 +162,35 @@ TabControl::~TabControl()
{ {
} }
void TabControl::setX(int x)
{
this->x = x;
initTabBar();
initTabPage();
dirty = true;
for (auto& c : controls)
{
c.first->onWindowResize();
c.second->onWindowResize();
}
}
void TabControl::setY(int y)
{
this->y = y;
initTabBar();
initTabPage();
dirty = true;
for (auto& c : controls)
{
c.first->onWindowResize();
c.second->onWindowResize();
}
}
void TabControl::draw() void TabControl::draw()
{ {
if (!dirty || !show)return; if (!dirty || !show)return;
// 在绘制 TabControl 之前,先恢复并更新背景快照:
if (hasSnap)
{
// 先回贴旧快照,清除之前的绘制
restBackground();
// 如位置或尺寸变化,丢弃旧快照并重新抓取
if (!saveBkImage || saveBkX != this->x || saveBkY != this->y || saveWidth != this->width || saveHeight != this->height)
{
discardBackground();
saveBackground(this->x, this->y, this->width, this->height);
}
}
else
{
// 首次绘制时抓取背景
saveBackground(this->x, this->y, this->width, this->height);
}
// 再次恢复最新背景,保证绘制区域干净
restBackground();
// 绘制画布背景和基本形状及其子画布控件 // 绘制画布背景和基本形状及其子画布控件
Canvas::draw(); Canvas::draw();
for (auto& c : controls) for (auto& c : controls)
@@ -196,8 +203,17 @@ void TabControl::draw()
c.second->setDirty(true); c.second->setDirty(true);
c.second->draw(); c.second->draw();
} }
dirty = false;
// 首次绘制时处理默认激活页签
if (IsFirstDraw)
{
if (defaultActivation >= 0 && defaultActivation < (int)controls.size())
controls[defaultActivation].first->setButtonClick(true);
else if (defaultActivation >= (int)controls.size())//索引越界则激活最后一个
controls[controls.size() - 1].first->setButtonClick(true);
IsFirstDraw = false;//避免重复处理
}
dirty = false;
} }
bool TabControl::handleEvent(const ExMessage& msg) bool TabControl::handleEvent(const ExMessage& msg)
@@ -211,7 +227,7 @@ bool TabControl::handleEvent(const ExMessage& msg)
break; break;
} }
for (auto& c : controls) for (auto& c : controls)
if(c.second->IsVisible()) if (c.second->IsVisible())
if (c.second->handleEvent(msg)) if (c.second->handleEvent(msg))
{ {
consume = true; consume = true;
@@ -232,7 +248,7 @@ void TabControl::add(std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>
controls[idx].first->enableTooltip(true); controls[idx].first->enableTooltip(true);
controls[idx].first->setbuttonMode(StellarX::ButtonMode::TOGGLE); controls[idx].first->setbuttonMode(StellarX::ButtonMode::TOGGLE);
controls[idx].first->setOnToggleOnListener([this,idx]() controls[idx].first->setOnToggleOnListener([this, idx]()
{ {
controls[idx].second->setIsVisible(true); controls[idx].second->setIsVisible(true);
controls[idx].second->onWindowResize(); controls[idx].second->onWindowResize();
@@ -246,7 +262,7 @@ void TabControl::add(std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>
} }
dirty = true; dirty = true;
}); });
controls[idx].first->setOnToggleOffListener([this,idx]() controls[idx].first->setOnToggleOffListener([this, idx]()
{ {
controls[idx].second->setIsVisible(false); controls[idx].second->setIsVisible(false);
@@ -265,12 +281,11 @@ void TabControl::add(std::string tabText, std::unique_ptr<Control> control)
if (tab.first->getButtonText() == tabText) if (tab.first->getButtonText() == tabText)
{ {
control->setParent(tab.second.get()); control->setParent(tab.second.get());
control->setIsVisible( tab.second->IsVisible()); control->setIsVisible(tab.second->IsVisible());
tab.second->addControl(std::move(control)); tab.second->addControl(std::move(control));
break; break;
} }
} }
} }
void TabControl::setTabPlacement(StellarX::TabPlacement placement) void TabControl::setTabPlacement(StellarX::TabPlacement placement)
@@ -279,7 +294,6 @@ void TabControl::setTabPlacement(StellarX::TabPlacement placement)
setDirty(true); setDirty(true);
initTabBar(); initTabBar();
initTabPage(); initTabPage();
} }
void TabControl::setTabBarHeight(int height) void TabControl::setTabBarHeight(int height)
@@ -293,16 +307,25 @@ void TabControl::setTabBarHeight(int height)
void TabControl::setIsVisible(bool visible) void TabControl::setIsVisible(bool visible)
{ {
// 先让基类 Canvas 处理自己的回贴/丢快照逻辑 // 先让基类 Canvas 处理自己的回贴/丢快照逻辑
Canvas::setIsVisible(visible); // <--- 新增 Canvas::setIsVisible(visible);
this->show = visible;
for (auto& tab : controls) for (auto& tab : controls)
{
if(true == visible)
{ {
tab.first->setIsVisible(visible); tab.first->setIsVisible(visible);
//页也要跟着关/开,否则它们会保留旧的 saveBkImage //页也要跟着关/开,否则它们会保留旧的 saveBkImage
tab.second->setIsVisible(visible); if (tab.first->isClicked())
tab.second->setIsVisible(true);
else
tab.second->setIsVisible(false);
tab.second->setDirty(true); tab.second->setDirty(true);
} }
else
{
tab.first->setIsVisible(visible);
tab.second->setIsVisible(visible);
}
}
} }
void TabControl::onWindowResize() void TabControl::onWindowResize()
@@ -336,20 +359,13 @@ int TabControl::getActiveIndex() const
void TabControl::setActiveIndex(int idx) void TabControl::setActiveIndex(int idx)
{ {
if (idx < 0 || idx > controls.size() - 1) return; if (IsFirstDraw)
if (controls[idx].first->getButtonMode() == StellarX::ButtonMode::DISABLED)return; defaultActivation = idx;
if (controls[idx].first->isClicked())
{
if (controls[idx].second->IsVisible())
return;
else
controls[idx].second->setIsVisible(true);
}
else else
{ {
if (idx >= 0 && idx < controls.size())
controls[idx].first->setButtonClick(true); controls[idx].first->setButtonClick(true);
} }
} }
int TabControl::count() const int TabControl::count() const
@@ -360,7 +376,7 @@ int TabControl::count() const
int TabControl::indexOf(const std::string& tabText) const int TabControl::indexOf(const std::string& tabText) const
{ {
int idx = -1; int idx = -1;
for(auto& c : controls) for (auto& c : controls)
{ {
idx++; idx++;
if (c.first->getButtonText() == tabText) if (c.first->getButtonText() == tabText)
@@ -388,7 +404,7 @@ void TabControl::requestRepaint(Control* parent)
{ {
if (control.first->isDirty() && control.first->IsVisible()) if (control.first->isDirty() && control.first->IsVisible())
control.first->draw(); control.first->draw();
else if (control.second->isDirty()&&control.second->IsVisible()) else if (control.second->isDirty() && control.second->IsVisible())
control.second->draw(); control.second->draw();
} }
} }

View File

@@ -5,7 +5,7 @@ Label::Label()
{ {
this->id = "Label"; this->id = "Label";
this->text = "默认标签"; this->text = "默认标签";
textStyle.color = RGB(0,0,0); textStyle.color = RGB(0, 0, 0);
textBkColor = RGB(255, 255, 255);; //默认白色背景 textBkColor = RGB(255, 255, 255);; //默认白色背景
} }
@@ -40,7 +40,7 @@ void Label::draw()
this->height = textheight(text.c_str()); this->height = textheight(text.c_str());
} }
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage) if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
saveBackground(this->x, this->y,this->width,this->height); saveBackground(this->x, this->y, this->width, this->height);
// 恢复背景(清除旧内容) // 恢复背景(清除旧内容)
restBackground(); restBackground();
outtextxy(x, y, LPCTSTR(text.c_str())); outtextxy(x, y, LPCTSTR(text.c_str()));
@@ -71,5 +71,4 @@ void Label::setText(std::string text)
{ {
this->text = text; this->text = text;
this->dirty = true; this->dirty = true;
} }

View File

@@ -26,12 +26,10 @@ void Table::drawTable()
dY = uY; dY = uY;
uY = dY + lineHeights.at(0) + TABLE_ROW_EXTRA; uY = dY + lineHeights.at(0) + TABLE_ROW_EXTRA;
} }
} }
void Table::drawHeader() void Table::drawHeader()
{ {
const int border = tableBorderWidth > 0 ? tableBorderWidth : 0; const int border = tableBorderWidth > 0 ? tableBorderWidth : 0;
// 内容区原点 = x+border, y+border // 内容区原点 = x+border, y+border
dX = x + border; dX = x + border;
@@ -132,7 +130,6 @@ void Table::initButton()
int nextW = textwidth(LPCTSTR(TABLE_STR_NEXT)) + padH * 2; int nextW = textwidth(LPCTSTR(TABLE_STR_NEXT)) + padH * 2;
int btnH = lblH + padV * 2; int btnH = lblH + padV * 2;
// 基于“页码标签”的矩形来摆放: // 基于“页码标签”的矩形来摆放:
// prev 在页码左侧 gap 处next 在右侧 gap 处Y 对齐 pY // prev 在页码左侧 gap 处next 在右侧 gap 处Y 对齐 pY
int prevX = pX - gap - prevW; int prevX = pX - gap - prevW;
@@ -199,7 +196,7 @@ void Table::initPageNum()
// 按理来说 x + (this->width - textW) / 2;就可以 // 按理来说 x + (this->width - textW) / 2;就可以
// 但是在绘制时发现控件偏右因此减去40 // 但是在绘制时发现控件偏右因此减去40
int textW = textwidth(LPCTSTR(pageNumtext.c_str())); int textW = textwidth(LPCTSTR(pageNumtext.c_str()));
pX = x + TABLE_PAGE_TEXT_OFFSET_X +(this->width - textW) / 2; pX = x + TABLE_PAGE_TEXT_OFFSET_X + (this->width - textW) / 2;
if (!pageNum) if (!pageNum)
pageNum = new Label(pX, pY, pageNumtext); pageNum = new Label(pX, pY, pageNumtext);
@@ -216,9 +213,8 @@ void Table::initPageNum()
void Table::drawPageNum() void Table::drawPageNum()
{ {
pageNumtext = ""; pageNumtext = "";
pageNumtext+= std::to_string(currentPage); pageNumtext += std::to_string(currentPage);
pageNumtext += "页/共"; pageNumtext += "页/共";
pageNumtext += std::to_string(totalPages); pageNumtext += std::to_string(totalPages);
pageNumtext += ""; pageNumtext += "";
@@ -229,12 +225,11 @@ void Table::drawPageNum()
if (StellarX::FillMode::Null == tableFillMode) if (StellarX::FillMode::Null == tableFillMode)
pageNum->setTextdisap(true); pageNum->setTextdisap(true);
pageNum->draw(); pageNum->draw();
} }
void Table::drawButton() void Table::drawButton()
{ {
if ((nullptr == prevButton || nullptr == nextButton)|| isNeedButtonAndPageNum) if ((nullptr == prevButton || nullptr == nextButton) || isNeedButtonAndPageNum)
initButton(); initButton();
this->prevButton->textStyle = this->textStyle; this->prevButton->textStyle = this->textStyle;
@@ -247,7 +242,20 @@ void Table::drawButton()
this->nextButton->setDirty(true); this->nextButton->setDirty(true);
prevButton->draw(); prevButton->draw();
nextButton->draw(); nextButton->draw();
}
void Table::setX(int x)
{
this->x = x;
isNeedButtonAndPageNum = true;
dirty = true;
}
void Table::setY(int y)
{
this->y = y;
isNeedButtonAndPageNum = true;
dirty = true;
} }
void Table::setWidth(int width) void Table::setWidth(int width)
@@ -269,7 +277,8 @@ void Table::setWidth(int width)
if (remainder > 0) { if (remainder > 0) {
change += 1; change += 1;
remainder -= 1; remainder -= 1;
} else if (remainder < 0) { }
else if (remainder < 0) {
change -= 1; change -= 1;
remainder += 1; remainder += 1;
} }
@@ -285,6 +294,7 @@ void Table::setWidth(int width)
void Table::setHeight(int height) void Table::setHeight(int height)
{ {
//高度不变
} }
Table::Table(int x, int y) Table::Table(int x, int y)
@@ -384,10 +394,13 @@ void Table::draw()
restBackground(); restBackground();
// 绘制表头 // 绘制表头
dX = x; //dX = x;
dY = y; //dY = y;
if(isNeedDrawHeaders)
{
drawHeader(); drawHeader();
this->isNeedDrawHeaders = false; this->isNeedDrawHeaders = false;
}
// 绘制当前页 // 绘制当前页
drawTable(); drawTable();
// 绘制页码标签 // 绘制页码标签
@@ -397,7 +410,6 @@ void Table::draw()
if (this->isShowPageButton) if (this->isShowPageButton)
drawButton(); drawButton();
// 恢复绘图状态 // 恢复绘图状态
restoreStyle(); restoreStyle();
dirty = false; // 标记不需要重绘 dirty = false; // 标记不需要重绘
@@ -406,14 +418,14 @@ void Table::draw()
bool Table::handleEvent(const ExMessage& msg) bool Table::handleEvent(const ExMessage& msg)
{ {
if(!show)return false; if (!show)return false;
bool consume = false; bool consume = false;
if(!this->isShowPageButton) if (!this->isShowPageButton)
return consume; return consume;
else else
{ {
if(prevButton)consume = prevButton->handleEvent(msg); if (prevButton)consume = prevButton->handleEvent(msg);
if (nextButton&&!consume) if (nextButton && !consume)
consume = nextButton->handleEvent(msg); consume = nextButton->handleEvent(msg);
} }
if (dirty) if (dirty)
@@ -431,7 +443,7 @@ void Table::setHeaders(std::initializer_list<std::string> headers)
dirty = true; dirty = true;
} }
void Table::setData( std::vector<std::string> data) void Table::setData(std::vector<std::string> data)
{ {
if (data.size() < headers.size()) if (data.size() < headers.size())
for (int i = 0; data.size() <= headers.size(); i++) for (int i = 0; data.size() <= headers.size(); i++)
@@ -444,12 +456,12 @@ void Table::setData( std::vector<std::string> data)
dirty = true; dirty = true;
} }
void Table::setData( std::initializer_list<std::vector<std::string>> data) void Table::setData(std::initializer_list<std::vector<std::string>> data)
{ {
for (auto lis : data) for (auto lis : data)
if (lis.size() < headers.size()) if (lis.size() < headers.size())
{ {
for (size_t i = lis.size(); i< headers.size(); i++) for (size_t i = lis.size(); i < headers.size(); i++)
lis.push_back(""); lis.push_back("");
this->data.push_back(lis); this->data.push_back(lis);
} }
@@ -523,6 +535,31 @@ void Table::setTableBorderWidth(int width)
this->dirty = true; this->dirty = true;
} }
void Table::clearHeaders()
{
this->headers.clear();
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
isNeedDrawHeaders = true; // 标记需要重新绘制表头
isNeedButtonAndPageNum = true;// 标记需要重新计算翻页按钮和页码信息
dirty = true;
}
void Table::clearData()
{
this->data.clear();
this->currentPage = 1;
this->totalPages = 1;
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
isNeedButtonAndPageNum = true;// 标记需要重新计算翻页按钮和页码信息
dirty = true;
}
void Table::resetTable()
{
clearHeaders();
clearData();
}
void Table::onWindowResize() void Table::onWindowResize()
{ {
Control::onWindowResize(); // 先处理自己 Control::onWindowResize(); // 先处理自己
@@ -601,5 +638,3 @@ int Table::getTableHeight() const
{ {
return 0; return 0;
} }

View File

@@ -1,9 +1,8 @@
// TextBox.cpp // TextBox.cpp
#include "TextBox.h" #include "TextBox.h"
TextBox::TextBox(int x, int y, int width, int height, std::string text, StellarX::TextBoxmode mode, StellarX::ControlShape shape) TextBox::TextBox(int x, int y, int width, int height, std::string text, StellarX::TextBoxmode mode, StellarX::ControlShape shape)
:Control(x,y,width,height),text(text), mode(mode), shape(shape) :Control(x, y, width, height), text(text), mode(mode), shape(shape)
{ {
this->id = "TextBox"; this->id = "TextBox";
} }
@@ -25,8 +24,22 @@ void TextBox::draw()
settextcolor(textStyle.color); settextcolor(textStyle.color);
setbkmode(TRANSPARENT); setbkmode(TRANSPARENT);
int text_width = textwidth(LPCTSTR(text.c_str()));
int text_height = textheight(LPCTSTR(text.c_str())); int text_width = 0;
int text_height = 0;
std::string pwdText;
if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
{
for (size_t i = 0; i < text.size(); ++i)
pwdText += '*';
text_width = textwidth(LPCTSTR(pwdText.c_str()));
text_height = textheight(LPCTSTR(pwdText.c_str()));
}
else
{
text_width = textwidth(LPCTSTR(text.c_str()));
text_height = textheight(LPCTSTR(text.c_str()));
}
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage) if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
saveBackground(this->x, this->y, this->width, this->height); saveBackground(this->x, this->y, this->width, this->height);
@@ -36,26 +49,29 @@ void TextBox::draw()
switch (shape) switch (shape)
{ {
case StellarX::ControlShape::RECTANGLE: case StellarX::ControlShape::RECTANGLE:
fillrectangle(x,y,x+width,y+height);//有边框填充矩形 fillrectangle(x, y, x + width, y + height);//有边框填充矩形
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str())); StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
: outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
break; break;
case StellarX::ControlShape::B_RECTANGLE: case StellarX::ControlShape::B_RECTANGLE:
solidrectangle(x, y, x + width, y + height);//无边框填充矩形 solidrectangle(x, y, x + width, y + height);//无边框填充矩形
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str())); StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
: outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
break; break;
case StellarX::ControlShape::ROUND_RECTANGLE: case StellarX::ControlShape::ROUND_RECTANGLE:
fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形 fillroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//有边框填充圆角矩形
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str())); StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
:outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
break; break;
case StellarX::ControlShape::B_ROUND_RECTANGLE: case StellarX::ControlShape::B_ROUND_RECTANGLE:
solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形 solidroundrect(x, y, x + width, y + height, rouRectangleSize.ROUND_RECTANGLEwidth, rouRectangleSize.ROUND_RECTANGLEheight);//无边框填充圆角矩形
outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str())); StellarX::TextBoxmode::PASSWORD_MODE == mode ? outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(pwdText.c_str()))
:outtextxy(x + 10, (y + (height - text_height) / 2), LPCTSTR(text.c_str()));
break; break;
} }
restoreStyle(); restoreStyle();
dirty = false; //标记不需要重绘 dirty = false; //标记不需要重绘
} }
} }
bool TextBox::handleEvent(const ExMessage& msg) bool TextBox::handleEvent(const ExMessage& msg)
@@ -77,9 +93,13 @@ bool TextBox::handleEvent(const ExMessage& msg)
if (hover && msg.message == WM_LBUTTONUP) if (hover && msg.message == WM_LBUTTONUP)
{ {
click = true; click = true;
if(StellarX::TextBoxmode::INPUT_MODE == mode) if (StellarX::TextBoxmode::INPUT_MODE == mode)
{ {
dirty = InputBox(LPTSTR(text.c_str()), (int)maxCharLen, "输入框", NULL, text.c_str(), NULL, NULL, false); char* temp = new char[maxCharLen + 1];
dirty = InputBox(temp, (int)maxCharLen+1, "输入框", NULL, text.c_str(), NULL, NULL, false);
if (dirty)text = temp;
delete[] temp;
temp = nullptr;
consume = true; consume = true;
} }
else if (StellarX::TextBoxmode::READONLY_MODE == mode) else if (StellarX::TextBoxmode::READONLY_MODE == mode)
@@ -88,6 +108,15 @@ bool TextBox::handleEvent(const ExMessage& msg)
InputBox(NULL, (int)maxCharLen, "输出框(输入无效!)", NULL, text.c_str(), NULL, NULL, false); InputBox(NULL, (int)maxCharLen, "输出框(输入无效!)", NULL, text.c_str(), NULL, NULL, false);
consume = true; consume = true;
} }
else if (StellarX::TextBoxmode::PASSWORD_MODE == mode)
{
char* temp = new char[maxCharLen + 1];
dirty = InputBox(temp, (int)maxCharLen+1, "输入框\n不可见输入,覆盖即可", NULL, NULL, NULL, NULL, false);
if (dirty)text = temp;
delete[] temp;
temp = nullptr;
consume = true;
}
flushmessage(EX_MOUSE | EX_KEY); flushmessage(EX_MOUSE | EX_KEY);
} }
if (dirty) if (dirty)
@@ -146,7 +175,7 @@ void TextBox::setTextBoxBk(COLORREF color)
void TextBox::setText(std::string text) void TextBox::setText(std::string text)
{ {
if(text.size() > maxCharLen) if (text.size() > maxCharLen)
text = text.substr(0, maxCharLen); text = text.substr(0, maxCharLen);
this->text = text; this->text = text;
this->dirty = true; this->dirty = true;
@@ -157,5 +186,3 @@ std::string TextBox::getText() const
{ {
return this->text; return this->text;
} }

View File

@@ -243,7 +243,10 @@ void Window::draw()
oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)&Window::WndProcThunk); oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)&Window::WndProcThunk);
procHooked = (oldWndProc != nullptr); procHooked = (oldWndProc != nullptr);
} }
if (!headline.empty())
{
SetWindowText(hWnd, headline.c_str());
}
ApplyResizableStyle(hWnd, useComposited); ApplyResizableStyle(hWnd, useComposited);
// 关闭类样式的整窗重绘标志(减少尺寸变化时的整窗 redraw // 关闭类样式的整窗重绘标志(减少尺寸变化时的整窗 redraw
@@ -438,7 +441,6 @@ int Window::runEventLoop()
} }
EndBatchDraw(); EndBatchDraw();
needredraw = false; needredraw = false;
} }
// —— 统一收口needResizeDirty 为真时执行一次性重绘)—— // —— 统一收口needResizeDirty 为真时执行一次性重绘)——
if (needResizeDirty) if (needResizeDirty)
@@ -470,6 +472,18 @@ int Window::runEventLoop()
// 调整底层画布尺寸 // 调整底层画布尺寸
if (finalW != width || finalH != height) if (finalW != width || finalH != height)
{ {
// 批量通知控件“窗口尺寸变化”,并标记重绘
for (auto& c : controls)
adaptiveLayout(c, finalH, finalW);
for (auto& d : dialogs)
{
if (auto dd = dynamic_cast<Dialog*>(d.get()))
{
dd->setDirty(true);
dd->setInitialization(true);
}
}
//重绘窗口
Resize(nullptr, finalW, finalH); Resize(nullptr, finalW, finalH);
// 重取一次实际客户区尺寸做确认 // 重取一次实际客户区尺寸做确认
@@ -499,21 +513,6 @@ int Window::runEventLoop()
height = renderHeight; height = renderHeight;
} }
// 批量通知控件“窗口尺寸变化”,并标记重绘
for (auto& c : controls)
{
adaptiveLayout(c,finalH,finalW);
c->onWindowResize();
}
for (auto& d : dialogs)
{
if (auto dd = dynamic_cast<Dialog*>(d.get()))
{
dd->setDirty(true);
dd->setInitialization(true);
}
}
// 统一批量绘制 // 统一批量绘制
for (auto& c : controls) c->draw(); for (auto& c : controls) c->draw();
for (auto& d : dialogs) d->draw(); for (auto& d : dialogs) d->draw();
@@ -705,7 +704,6 @@ void Window::pumpResizeIfNeeded()
{ {
setbkcolor(wBkcolor); setbkcolor(wBkcolor);
cleardevice(); cleardevice();
} }
width = rc.right - rc.left; height = rc.bottom - rc.top; width = rc.right - rc.left; height = rc.bottom - rc.top;
@@ -809,11 +807,12 @@ void Window::adaptiveLayout(std::unique_ptr<Control>& c, const int finalH, const
c->setHeight(c->getLocalHeight()); c->setHeight(c->getLocalHeight());
c->setY(finalH - origBottomDist - c->getHeight()); c->setY(finalH - origBottomDist - c->getHeight());
} }
else { else
{
// 垂直无锚点:默认为顶部定位,高度固定 // 垂直无锚点:默认为顶部定位,高度固定
c->setY(c->getLocalY()); c->setY(c->getLocalY());
c->setHeight(c->getLocalHeight()); c->setHeight(c->getLocalHeight());
} }
} }
c->onWindowResize();
} }