Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 58d4e8ab2f | |||
| 5420bfd644 |
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
[中文文档](CHANGELOG.md)
|
||||
|
||||
## [v2.3.0] - 2025-11-18
|
||||
|
||||
### ✨ Added
|
||||
|
||||
- Introduced `LayoutMode` adaptive layout mode and `Anchor` anchor points. Controls can now call `setLayoutMode` to set layout mode and `steAnchor` to set anchor points, enabling controls to adapt to window changes during resizing. Dialog controls only recalculate their position during window resizing to maintain center alignment.
|
||||
- Added `adaptiveLayout()` API to `Window`, called by the main event loop to recalculate control positions and sizes based on anchor points during window resizing, enabling dual-anchored controls (left-right or top-bottom) to automatically stretch with window changes.
|
||||
|
||||
### ⚙️ Changed
|
||||
|
||||
- **Optimized Window Resizing Mechanism**: Refactored `WndProcThunk`, `runEventLoop`, and `pumpResizeIfNeeded` to uniformly record window size changes and perform one-time repainting at the end of the event loop, preventing jitter and sequencing issues caused by repeated drawing during resizing.
|
||||
- **Added Dialog Size Scheduling Interface**: Introduced `Window::scheduleResizeFromModal()`, used in conjunction with `pumpResizeIfNeeded()`. During modal dialog display, the parent window can update client area size in real time and relayout child controls during unified finalization, while the dialog maintains its original size.
|
||||
- **Redraw Sequence Optimization**: Replaced `InvalidateRect` with `ValidateRect` during the finalization phase after window size changes, preventing the system from sending additional `WM_PAINT` messages that could cause reentrant drawing.
|
||||
- **Other Improvements**: Fixed delayed background snapshot updates for table pagination buttons and page number labels; improved dialog background capture logic.
|
||||
|
||||
### ✅ Fixed
|
||||
|
||||
- **Modal Dialog Resizing Fix**: Resolved the issue where window resizing during modal dialog display prevented underlying controls from updating their sizes and positions according to anchor points; simultaneously eliminated ghosting artifacts caused by repeated dialog redrawing.
|
||||
- **Drawing Sequence Disorder Fix**: Addressed sporadic drawing sequence disorders, control ghosting, and border flickering during window resizing, ensuring controls are drawn in the order they were added.
|
||||
- **Stability Fixes**: Corrected abnormal frames caused by sudden window size changes in certain scenarios; resolved delayed background snapshot updates for tables and dialogs under edge conditions.
|
||||
|
||||
## [v2.2.2] - 2025 - 11- 08
|
||||
|
||||
### ⚙️ Changes
|
||||
|
||||
- Modified the coordinate transfer method for the Canvas container. Child control coordinates are now passed as relative coordinates (with the origin at the top-left corner of the container, obtainable via the `getX`/`getY` interface), instead of the original global coordinates. Child control coordinates can now be set to negative values.
|
||||
- The example under `examples\register-viewer` has been updated to the latest version, aligning container child controls to use relative coordinates.
|
||||
|
||||
### ✅ Fixes
|
||||
|
||||
- Fixed jittering/bouncing and flickering when resizing the window (left/top edges):
|
||||
- `WM_SIZING` only clamps the minimum size; `WM_GETMINMAXINFO` sets the window-level minimum tracking size.
|
||||
- Freezes redrawing during dragging and handles resizing uniformly upon release; `WM_SIZE` only records the new size without interfering with drawing.
|
||||
- Disabled `WM_ERASEBKGND` background erasing and removed `CS_HREDRAW`/`CS_VREDRAW` to reduce flickering.
|
||||
- Fixed issues related to dialog boxes:
|
||||
- Resolved occasional residual functional buttons after closing a dialog.
|
||||
- Fixed issues where window resizing failed to redraw or displayed a corrupted background when a modal dialog was active.
|
||||
- Fixed delayed updates of background snapshots for the table control's pagination buttons and page number labels during window changes.
|
||||
|
||||
## [v2.2.1] - 2025-11-04
|
||||
|
||||
==This release is a hotfix for v2.2.0==
|
||||
|
||||
@@ -7,6 +7,45 @@ StellarX 项目所有显著的变化都将被记录在这个文件中。
|
||||
|
||||
[English document](CHANGELOG.en.md)
|
||||
|
||||
## [v2.3.0] - 2025 - 11 - 18
|
||||
|
||||
### ✨ 新增
|
||||
|
||||
- 新增`LayoutMode `自适应布局模式和`Anchor`锚点,控件中可以调用 `setLayoutMode`
|
||||
设置布局模式以及`steAnchor`设置锚点,达到窗口拉伸时控件自适应窗口变化。对话框控件在窗口变化时只会重新计算位置,来保证居中显示
|
||||
- `Window`新增`adaptiveLayout()`这个API由主事件循环调用,在窗口拉伸时根据锚点重新计算控件位置和尺寸,使左右/上下双锚定控件随窗口变化自动伸缩。
|
||||
|
||||
### ⚙️ 变更
|
||||
|
||||
- **优化窗口尺寸调整机制**:重构 `WndProcThunk`、`runEventLoop` 和 `pumpResizeIfNeeded`,统一记录窗口尺寸变化并在事件循环尾部一次性重绘,避免拉伸过程中重复绘制引发抖动与顺序错乱。
|
||||
- **新增对话框尺寸调度接口**:引入 `Window::scheduleResizeFromModal()`,配合 `pumpResizeIfNeeded()` 使用。模态对话框显示期间,父窗口可实时更新客户区尺寸并在统一收口时重新布局子控件,对话框自身尺寸保持不变。
|
||||
- **重绘顺序优化**:在窗口尺寸变化后的收口阶段使用 `ValidateRect` 代替 `InvalidateRect`,避免系统再次发送 `WM_PAINT` 导致重入绘制。
|
||||
- **其他改进**:修复表格翻页按钮与页码标签等元素背景快照更新不及时的问题;改进对话框背景捕捉逻辑。
|
||||
|
||||
### ✅ 修复
|
||||
|
||||
- **模态对话框拉伸修复**:解决了模态对话框弹出时,窗口拉伸无法让底层控件按照锚点更新尺寸和位置的问题;同时避免对话框反复重绘导致残影。
|
||||
- **绘制顺序错乱修复**:解决窗口拉伸时偶发的绘制顺序紊乱、控件残影和边框闪烁问题,确保控件按添加顺序依次绘制。
|
||||
- **稳定性修复**:修正某些情况下窗口尺寸突变导致的异常帧;解决表格和对话框背景快照在边界条件下未及时更新的问题。
|
||||
|
||||
## [v2.2.2] - 2025 - 11- 08
|
||||
|
||||
### ⚙️ 变更
|
||||
|
||||
- Canvas容器坐标传递方式改变,子控件坐标由原来的传递全局坐标改为传递相对坐标(坐标原点为容器的左上角坐标可通过getX/Y接口获得)可以设置子控件坐标为负值
|
||||
- examples\register-viewer下的案例已同步修改为最新,同步容器子控件为相对坐标
|
||||
|
||||
### ✅ 修复
|
||||
|
||||
- 修复窗口拉伸(左/上边)时的抖动/弹回与闪烁
|
||||
- `WM_SIZING` 仅做最小尺寸夹紧;`WM_GETMINMAXINFO` 设置窗口级最小轨迹
|
||||
- 拖拽期冻结重绘,松手统一收口;`WM_SIZE` 只记录尺寸不抢绘制
|
||||
- 禁用 `WM_ERASEBKGND` 擦背景并移除 `CS_HREDRAW/CS_VREDRAW`,减少闪烁
|
||||
- 对话框的相关问题
|
||||
- 对话框关闭后概率出现功能按钮残留
|
||||
- 模态对话框触发时,窗口拉伸无法重绘或背景错乱
|
||||
- 表格控件在窗口变化时其翻页按钮和页码标签背景快照更新不及时的问题
|
||||
|
||||
## [v2.2.1] - 2025 - 11 - 4
|
||||
|
||||
==此版本为v2.2.0的修复版本==
|
||||
|
||||
+25
-14
@@ -7,8 +7,8 @@
|
||||

|
||||
[](https://github.com/Ysm-04/StellarX)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
@@ -25,22 +25,28 @@ This is a **teaching-grade and tooling-grade** framework that helps developers u
|
||||
|
||||
------
|
||||
|
||||
## **🆕 v2.2.1 (Hotfix for v2.2.0)**
|
||||
## 🆕 V2.3.0 - Major Update
|
||||
|
||||
- Addressed a flickering issue that occurred when using the Canvas and TabControl containers.
|
||||
- Fixed issues where border remnants and functional buttons could persist after closing a Dialog.
|
||||
**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.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
For details, please refer to the [CHANGELOG.en](CHANGELOG.en.md).
|
||||
|
||||
## What’s new in v2.2.0
|
||||
|
||||
- **New TabControl for multi-page tabbed UIs:** With `TabControl`, it’s easy to create a tabbed layout. Tabs can be arranged on the top, bottom, left, or right, and clicking switches the displayed page. Suitable for settings panels and multi-view switching.
|
||||
- **Enhanced control show/hide and resize responsiveness:** All controls now share a unified interface (`setIsVisible`) to toggle visibility. When a container control is hidden, its child controls automatically hide/show with it. Meanwhile, we introduce `onWindowResize` for controls to respond to window size changes so elements update in sync after resizing, eliminating artifacts or misalignment.
|
||||
- **Refined text-style mechanism:** The Label control now uses a unified `ControlText` style structure. Developers can easily customize font, color, size, etc. (replacing older interfaces, and more flexible). Button Tooltips also support richer customization and different texts for toggle states.
|
||||
- **Other improvements:** Dialog management gains de-duplication to prevent identical prompts from popping up repeatedly. Several bug fixes and refresh optimizations further improve stability.
|
||||
|
||||
See `CHANGELOG.md / CHANGELOG.en.md` for the full list.
|
||||
|
||||
------
|
||||
|
||||
## 📦 Project Structure & Design Philosophy
|
||||
@@ -181,6 +187,11 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
| `MessageBoxResult` | Result | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
||||
| `TabPlacement` | Tab position | `Top`, `Bottom`, `Left`, `Right` |
|
||||
|
||||
| Enum | Description | Common values |
|
||||
| ------------ | ---------------------- | -------------------------------------------- |
|
||||
| `LayoutMode` | 窗口布局模式 | `Fixed`, `AnchorToEdges` |
|
||||
| `Anchor` | 控件相对于父容器的锚点 | `NoAnchor` ,`Left` , `Right`, `Top`,`Bottom` |
|
||||
|
||||
### Structs
|
||||
|
||||
| Struct | Description |
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||

|
||||
[](https://github.com/Ysm-04/StellarX)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
@@ -30,24 +30,27 @@
|
||||
|
||||
---
|
||||
|
||||
## 🆕v2.2.1(v2.2.0修复版)
|
||||
## 🆕V2.3.0——重要更新
|
||||
|
||||
- 解决了使用Canvas和TabControl容器时,出现频闪问题
|
||||
**本版本是一次重大更新,增加了响应式布局系统,由静态布局转变为动态布局,并且彻底修复了之前存在的由重入重绘导致的概率出现的渲染错乱问题**
|
||||
|
||||
- 修复了Dialog对话框关闭时概率出边边框残留和功能按钮残留问题
|
||||
- **优化窗口尺寸调节机制**:重构 `WndProcThunk`、`runEventLoop` 和 `pumpResizeIfNeeded`,统一记录尺寸变化并在事件循环末尾集中重绘,避免重复重绘导致的抖动和顺序错乱。
|
||||
|
||||
详情参考[更新日志](CHANGELOG.md)
|
||||
- **新增对话框尺寸调度接口**:引入 `Window::scheduleResizeFromModal()` 与 `pumpResizeIfNeeded()` 的组合,模态对话框在拉伸期间也可通知父窗口更新尺寸。底层控件将在统一收口时重新布局,而对话框自身保持尺寸不变。
|
||||
|
||||
- **自适应布局改进**:内部新增 `adaptiveLayout()` 函数,按照锚点重新计算控件位置和尺寸,使双锚定(左右或上下)控件随窗口变化自适应伸缩。
|
||||
|
||||
- **修复模态对话框拉伸问题**:解决模态对话框打开时,窗口拉伸导致底层控件无法根据锚点更新的位置和尺寸的问题;同时避免对话框反复重绘导致的残影。
|
||||
|
||||
## V2.2.0 有何变化
|
||||
- **进一步解决绘制顺序错乱**:拉伸过程中采用 `ValidateRect` 替代 `InvalidateRect`,确保窗口仅在一次统一收口绘制后标记为有效,杜绝系统再次触发 `WM_PAINT` 造成重入。
|
||||
|
||||
- **新增 TabControl 控件,实现多页面选项卡界面:** 通过 `TabControl` 可以轻松创建选项卡式布局,支持页签在上下左右排列、点击切换显示不同内容页面。适用于设置面板、多视图切换等场景。
|
||||
- **控件显隐与布局响应能力增强:** 现在所有控件都可以使用统一接口动态隐藏或显示(`setIsVisible`),容器控件隐藏时其内部子控件会自动随之隐藏/显示。与此同时,引入控件对窗口尺寸变化的响应机制(`onWindowResize`),窗口拉伸后界面各元素可协调更新,杜绝拉伸过程中出现残影或错位。
|
||||
- **文本样式机制完善:** Label 控件改用统一的文本样式结构 `ControlText`,开发者可方便地设置字体、颜色、大小等属性来定制 Label 的外观(替代旧接口,更加灵活)。Button 的 Tooltip 提示也支持更丰富的定制和针对切换状态的不同提示文本。
|
||||
- **其他改进:** 框架底层的对话框管理增加了防重复弹出相同提示的机制,修复了一些细节 Bug 并优化了刷新效率,进一步提升了稳定性。
|
||||
- 其他修复:修正表格和对话框背景快照某些边界情况下的更新不及时问题。
|
||||
|
||||
详见 `CHANGELOG.md / CHANGELOG.en.md` 获取完整更新列表。
|
||||

|
||||
|
||||

|
||||
|
||||
详细变更请参阅[更新日志](CHANGELOG.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -189,6 +192,13 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
| `MessageBoxResult` | 结果 | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
||||
| `TabPlacement` | 页签位置 | `Top`,`Bottom`,`Left`,`Right` |
|
||||
|
||||
| 枚举 | 描述 | 常用值 |
|
||||
| ------------ | ---------------------- | -------------------------------------------- |
|
||||
| `LayoutMode` | 窗口布局模式 | `Fixed`, `AnchorToEdges` |
|
||||
| Anchor | 控件相对于父容器的锚点 | `NoAnchor` ,`Left` , `Right`, `Top`,`Bottom` |
|
||||
|
||||
|
||||
|
||||
### 结构体
|
||||
|
||||
| 结构体 | 描述 |
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
auto blackColor = RGB(202, 255, 255);
|
||||
char initData[33] = "00000000000000000000000000000000";//初始数据
|
||||
bool gSigned = false; //是否为有符号数
|
||||
void main()
|
||||
{
|
||||
Window mainWindow(700,500,NULL,RGB(255,255,255), "寄存器查看工具 V1.0——我在人间做废物 (同类工具定制:3150131407(Q / V))");
|
||||
|
||||
int main()
|
||||
{
|
||||
Window mainWindow(700, 510, NULL, RGB(255, 255, 255), "寄存器查看工具 V1.0——我在人间做废物 (同类工具定制:3150131407(Q / V))");
|
||||
|
||||
//选择区控件
|
||||
auto selectionAreaLabel = std::make_unique<Label>(18, 0,"32位选择区");
|
||||
auto selectionAreaLabel = std::make_unique<Label>(18, 0, "32位选择区");
|
||||
selectionAreaLabel->setTextdisap(true);
|
||||
std::vector<std::unique_ptr<Label>>selectionAreaButtonLabel;
|
||||
std::vector<std::unique_ptr<Button>>selectionAreaButton;
|
||||
@@ -21,22 +21,22 @@ void main()
|
||||
|
||||
selectionArea->setCanvasBkColor(blackColor);
|
||||
selectionArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
|
||||
for (int y = 0; y < 2; y ++)
|
||||
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (int x = 0; x <16; x++)
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
if (0 == y)
|
||||
{
|
||||
selectionAreaButtonLabel.push_back(std::make_unique<Label>(x * 35 + 40 + 28 * (x / 4), 26, "", RGB(208, 208, 208)));
|
||||
selectionAreaButtonLabel.push_back(std::make_unique<Label>(x * 35 + 25 + 28 * (x / 4), 26, "", RGB(208, 208, 208)));
|
||||
os << std::setw(2) << std::setfill('0') << 31 - x;
|
||||
selectionAreaButtonLabel.back()->setText(os.str());
|
||||
selectionAreaButtonLabel.back()->setTextdisap(true);
|
||||
|
||||
selectionAreaButton.push_back(
|
||||
std::make_unique<Button>(x * 35 + 42 + 28 * (x / 4), 58,20,32,"0",
|
||||
blackColor,RGB(171, 196, 220),StellarX::ButtonMode::TOGGLE));
|
||||
std::make_unique<Button>(x * 35 + 27 + 28 * (x / 4), 58, 25, 30, "0",
|
||||
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE));
|
||||
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
||||
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
||||
@@ -55,19 +55,19 @@ void main()
|
||||
}
|
||||
else
|
||||
{
|
||||
selectionAreaButtonLabel.push_back(std::make_unique<Label>(x * 35 + 40 + 28 * (x / 4), 90, "", RGB(208, 208, 208)));
|
||||
os << std::setw(2) << std::setfill('0') << 15-x;
|
||||
selectionAreaButtonLabel.push_back(std::make_unique<Label>(x * 35 + 25 + 28 * (x / 4), 90, "", RGB(208, 208, 208)));
|
||||
os << std::setw(2) << std::setfill('0') << 15 - x;
|
||||
selectionAreaButtonLabel.back()->setText(os.str());
|
||||
selectionAreaButtonLabel.back()->setTextdisap(true);
|
||||
|
||||
selectionAreaButton.push_back(
|
||||
std::make_unique<Button>(x * 35 + 42 + 28 * (x / 4), 120, 20, 32, "0",
|
||||
std::make_unique<Button>(x * 35 + 27 + 28 * (x / 4), 120, 25, 30, "0",
|
||||
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE));
|
||||
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
||||
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
||||
int k =15 - x;
|
||||
selectionAreaButton.back()->setOnToggleOnListener([k,btn = selectionAreaButton_ptr.back()]()
|
||||
int k = 15 - x;
|
||||
selectionAreaButton.back()->setOnToggleOnListener([k, btn = selectionAreaButton_ptr.back()]()
|
||||
{
|
||||
btn->setButtonText("1");
|
||||
initData[k] = '1';
|
||||
@@ -78,8 +78,8 @@ void main()
|
||||
initData[k] = '0';
|
||||
});
|
||||
}
|
||||
os.str("");
|
||||
os.clear();
|
||||
os.str("");
|
||||
os.clear();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -90,25 +90,34 @@ void main()
|
||||
selectionArea->addControl(std::move(s));
|
||||
//功能区控件
|
||||
//功能区总容器
|
||||
auto function = std::make_unique<Canvas>(0, 0, 0, 0);
|
||||
auto function = std::make_unique<Canvas>(10, 170, 680, 70);
|
||||
function->setCanvasfillMode(StellarX::FillMode::Null);
|
||||
|
||||
auto bitInvert = std::make_unique<Canvas>(10,170,220,70);
|
||||
auto leftShift = std::make_unique<Canvas>(240, 170, 220, 70);
|
||||
auto rightShift = std::make_unique<Canvas>(470, 170, 220, 70);
|
||||
function->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
function->setCanvasBkColor(blackColor);
|
||||
auto bitInvert_que = std::make_unique<Canvas>(0, 0, 220, 70);
|
||||
auto leftShift_que = std::make_unique<Canvas>(230, 0, 220, 70);
|
||||
auto rightShift_que = std::make_unique<Canvas>(460, 0, 220, 70);
|
||||
|
||||
auto bitInvert = bitInvert_que.get();
|
||||
auto leftShift = leftShift_que.get();
|
||||
auto rightShift = rightShift_que.get();
|
||||
|
||||
bitInvert->setCanvasBkColor(blackColor);
|
||||
bitInvert->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
leftShift->setCanvasBkColor(blackColor);
|
||||
leftShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
rightShift->setCanvasBkColor(blackColor);
|
||||
rightShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
|
||||
|
||||
auto bitInvertLabel = std::make_unique<Label>(18,160,"位取反");
|
||||
|
||||
function->addControl(std::move(bitInvert_que));
|
||||
function->addControl(std::move(leftShift_que));
|
||||
function->addControl(std::move(rightShift_que));
|
||||
|
||||
auto bitInvertLabel = std::make_unique<Label>(13, -10, "位取反");
|
||||
bitInvertLabel->setTextdisap(true);
|
||||
auto leftShiftLabel = std::make_unique<Label>(248, 160, "左移位");
|
||||
auto leftShiftLabel = std::make_unique<Label>(13, -10, "左移位");
|
||||
leftShiftLabel->setTextdisap(true);
|
||||
auto rightShiftLabel = std::make_unique<Label>(478, 160, "右移位");
|
||||
auto rightShiftLabel = std::make_unique<Label>(13, -10, "右移位");
|
||||
rightShiftLabel->setTextdisap(true);
|
||||
|
||||
// ====== 公用小工具======
|
||||
@@ -139,22 +148,22 @@ void main()
|
||||
|
||||
//取反区控件
|
||||
std::array<std::unique_ptr<Label>, 4> bitInvertFunctionLabel;
|
||||
bitInvertFunctionLabel[0] = std::make_unique<Label>(35, 180, "低位");
|
||||
bitInvertFunctionLabel[1] = std::make_unique<Label>(90, 180, "高位");
|
||||
bitInvertFunctionLabel[2] = std::make_unique<Label>(15, 198, "从");
|
||||
bitInvertFunctionLabel[3] = std::make_unique<Label>(75, 198, "到");
|
||||
bitInvertFunctionLabel[0] = std::make_unique<Label>(30, 10, "低位");
|
||||
bitInvertFunctionLabel[1] = std::make_unique<Label>(90, 10, "高位");
|
||||
bitInvertFunctionLabel[2] = std::make_unique<Label>(15, 38, "从");
|
||||
bitInvertFunctionLabel[3] = std::make_unique<Label>(75, 38, "到");
|
||||
|
||||
std::array<std::unique_ptr<TextBox>, 2> bitInvertFunctionTextBox;
|
||||
bitInvertFunctionTextBox[0] = std::make_unique<TextBox>(35, 203, 35, 30, "0");
|
||||
bitInvertFunctionTextBox[1] = std::make_unique<TextBox>(95, 203, 35, 30, "0");
|
||||
bitInvertFunctionTextBox[0] = std::make_unique<TextBox>(35, 35, 35, 30, "0");
|
||||
bitInvertFunctionTextBox[1] = std::make_unique<TextBox>(95, 35, 35, 30, "0");
|
||||
auto invL = bitInvertFunctionTextBox[0].get();
|
||||
auto invH = bitInvertFunctionTextBox[1].get();
|
||||
auto bitInvertFunctionButton = std::make_unique<Button>(150,195, 70, 35, "位取反",
|
||||
auto bitInvertFunctionButton = std::make_unique<Button>(135, 35, 80, 30, "位取反",
|
||||
blackColor, RGB(171, 196, 220));
|
||||
bitInvertFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||
bitInvertFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto bitInvertFunctionButton_ptr = bitInvertFunctionButton.get();
|
||||
|
||||
|
||||
bitInvert->addControl(std::move(bitInvertFunctionButton));
|
||||
bitInvert->addControl(std::move(bitInvertLabel));
|
||||
for (auto& b : bitInvertFunctionTextBox)
|
||||
@@ -171,22 +180,22 @@ void main()
|
||||
bitInvert->addControl(std::move(b));
|
||||
}
|
||||
//左移控件
|
||||
auto leftShiftFunctionLabel = std::make_unique<Label>(435, 198, "位");
|
||||
auto leftShiftFunctionLabel = std::make_unique<Label>(198, 30, "位");
|
||||
leftShiftFunctionLabel->setTextdisap(true);
|
||||
|
||||
auto leftShiftFunctionTextBox = std::make_unique<TextBox>(325, 195, 100, 30, "0");
|
||||
auto leftShiftFunctionTextBox = std::make_unique<TextBox>(90, 30, 100, 30, "0");
|
||||
leftShiftFunctionTextBox->setMaxCharLen(3);
|
||||
leftShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
||||
leftShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
||||
leftShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto shlBox = leftShiftFunctionTextBox.get();
|
||||
auto leftShiftFunctionButton = std::make_unique<Button>(250, 195, 60, 30, "左移",
|
||||
auto leftShiftFunctionButton = std::make_unique<Button>(15, 30, 60, 30, "左移",
|
||||
blackColor, RGB(171, 196, 220));
|
||||
leftShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||
leftShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto leftShiftFunctionButton_ptr = leftShiftFunctionButton.get();
|
||||
|
||||
|
||||
|
||||
|
||||
leftShift->addControl(std::move(leftShiftFunctionButton));
|
||||
leftShift->addControl(std::move(leftShiftFunctionTextBox));
|
||||
|
||||
@@ -194,47 +203,44 @@ void main()
|
||||
leftShift->addControl(std::move(leftShiftFunctionLabel));
|
||||
|
||||
//右移控件
|
||||
auto rightShiftFunctionLabel = std::make_unique<Label>(665, 198, "位");
|
||||
auto rightShiftFunctionLabel = std::make_unique<Label>(198, 30, "位");
|
||||
rightShiftFunctionLabel->setTextdisap(true);
|
||||
auto rightShiftFunctionTextBox = std::make_unique<TextBox>(555, 195, 100, 30, "0");
|
||||
auto rightShiftFunctionTextBox = std::make_unique<TextBox>(90, 30, 100, 30, "0");
|
||||
rightShiftFunctionTextBox->setMaxCharLen(3);
|
||||
rightShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
||||
rightShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
||||
rightShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto shrBox = rightShiftFunctionTextBox.get();
|
||||
|
||||
auto rightShiftFunctionButton = std::make_unique<Button>(480, 195, 60, 30, "右移",
|
||||
auto rightShiftFunctionButton = std::make_unique<Button>(15, 30, 60, 30, "右移",
|
||||
blackColor, RGB(171, 196, 220));
|
||||
rightShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||
rightShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto rightShiftFunctionButton_ptr = rightShiftFunctionButton.get();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
rightShift->addControl(std::move(rightShiftFunctionButton));
|
||||
|
||||
rightShift->addControl(std::move(rightShiftFunctionTextBox));
|
||||
rightShift->addControl(std::move(rightShiftLabel));
|
||||
rightShift->addControl(std::move(rightShiftFunctionLabel));
|
||||
|
||||
function->addControl(std::move(bitInvert));
|
||||
function->addControl(std::move(leftShift));
|
||||
function->addControl(std::move(rightShift));
|
||||
|
||||
|
||||
//显示区控件
|
||||
//数值显示
|
||||
auto NumericalDisplayArea = std::make_unique<Canvas>(10, 255, 680, 70);
|
||||
NumericalDisplayArea->setCanvasBkColor(blackColor);
|
||||
NumericalDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
|
||||
|
||||
std::array<std::unique_ptr<Label>, 3> NumericalDisplayAreaLabel;
|
||||
NumericalDisplayAreaLabel[0] = std::make_unique<Label>(18, 245, "数值显示区");
|
||||
NumericalDisplayAreaLabel[1] = std::make_unique<Label>(20, 278, "十六进制");
|
||||
NumericalDisplayAreaLabel[2] = std::make_unique<Label>(330, 278, "十进制");
|
||||
NumericalDisplayAreaLabel[0] = std::make_unique<Label>(18, -10, "数值显示区");
|
||||
NumericalDisplayAreaLabel[1] = std::make_unique<Label>(20, 25, "十六进制");
|
||||
NumericalDisplayAreaLabel[2] = std::make_unique<Label>(330, 25, "十进制");
|
||||
|
||||
std::array<std::unique_ptr<TextBox>, 2> NumericalDisplayAreaTextBox;
|
||||
NumericalDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 275, 200, 30, "0");
|
||||
NumericalDisplayAreaTextBox[1] = std::make_unique<TextBox>(400, 275, 200, 30, "0");
|
||||
NumericalDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 25, 200, 30, "0");
|
||||
NumericalDisplayAreaTextBox[1] = std::make_unique<TextBox>(400, 25, 200, 30, "0");
|
||||
auto hex = NumericalDisplayAreaTextBox[0].get();
|
||||
auto dec = NumericalDisplayAreaTextBox[1].get();
|
||||
|
||||
@@ -257,15 +263,15 @@ void main()
|
||||
auto BinaryDisplayArea = std::make_unique<Canvas>(10, 335, 680, 110);
|
||||
BinaryDisplayArea->setCanvasBkColor(blackColor);
|
||||
BinaryDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
|
||||
|
||||
std::array<std::unique_ptr<Label>, 3> BinaryDisplayAreaLabel;
|
||||
BinaryDisplayAreaLabel[0] = std::make_unique<Label>(18, 325, "二进制显示区");
|
||||
BinaryDisplayAreaLabel[1] = std::make_unique<Label>(35, 353, "上次值");
|
||||
BinaryDisplayAreaLabel[2] = std::make_unique<Label>(35, 400, "本次值");
|
||||
BinaryDisplayAreaLabel[0] = std::make_unique<Label>(18, -10, "二进制显示区");
|
||||
BinaryDisplayAreaLabel[1] = std::make_unique<Label>(35, 20, "上次值");
|
||||
BinaryDisplayAreaLabel[2] = std::make_unique<Label>(35, 67, "本次值");
|
||||
|
||||
std::array<std::unique_ptr<TextBox>, 2> BinaryDisplayAreaTextBox;
|
||||
BinaryDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 350, 520, 30, "0000_0000_0000_0000_0000_0000_0000_0000");
|
||||
BinaryDisplayAreaTextBox[1] = std::make_unique<TextBox>(110, 400, 520, 30, "0000_0000_0000_0000_0000_0000_0000_0000");
|
||||
BinaryDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 20, 520, 30, "0000_0000_0000_0000_0000_0000_0000_0000");
|
||||
BinaryDisplayAreaTextBox[1] = std::make_unique<TextBox>(110, 67, 520, 30, "0000_0000_0000_0000_0000_0000_0000_0000");
|
||||
auto Last = BinaryDisplayAreaTextBox[0].get();
|
||||
auto This = BinaryDisplayAreaTextBox[1].get();
|
||||
|
||||
@@ -366,17 +372,17 @@ void main()
|
||||
auto configuration = std::make_unique<Canvas>(10, 455, 680, 40);
|
||||
configuration->setCanvasBkColor(blackColor);
|
||||
configuration->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||
|
||||
auto configurationLabel = std::make_unique<Label>(20, 445, "配置区");
|
||||
|
||||
auto configurationLabel = std::make_unique<Label>(20, -10, "配置区");
|
||||
configurationLabel->setTextdisap(true);
|
||||
|
||||
std::array<std::unique_ptr<Button>,2> configurationButton;
|
||||
configurationButton[0] = std::make_unique<Button>(450, 465, 80, 20, "一键置0",
|
||||
std::array<std::unique_ptr<Button>, 2> configurationButton;
|
||||
configurationButton[0] = std::make_unique<Button>(420, 10, 90, 20, "一键置0",
|
||||
blackColor, RGB(171, 196, 220));
|
||||
configurationButton[0]->textStyle.color = RGB(226, 116, 152);
|
||||
configurationButton[0]->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
|
||||
configurationButton[1] = std::make_unique<Button>(550, 465, 80, 20, "一键置1",
|
||||
|
||||
configurationButton[1] = std::make_unique<Button>(530, 10, 90, 20, "一键置1",
|
||||
blackColor, RGB(171, 196, 220));
|
||||
configurationButton[1]->textStyle.color = RGB(226, 116, 152);
|
||||
configurationButton[1]->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
@@ -419,16 +425,16 @@ void main()
|
||||
|
||||
|
||||
auto signedToggle = std::make_unique<Button>(
|
||||
350, 465, 80, 20, "无符号",
|
||||
330, 10, 80, 20, "无符号",
|
||||
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE);
|
||||
signedToggle->textStyle.color = RGB(226, 116, 152);
|
||||
signedToggle->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||
auto* signedTogglePtr = signedToggle.get();
|
||||
|
||||
|
||||
signedTogglePtr->setOnToggleOnListener([&]() {
|
||||
gSigned = true;
|
||||
signedTogglePtr->setButtonText("有符号");
|
||||
|
||||
StellarX::MessageBox::showModal(mainWindow, "有符号模式下,\n最高位为符号位,\n其余位为数值位。", "有符号模式");
|
||||
// 立即刷新十进制显示:用当前位图算出新值,仅改 dec
|
||||
auto cur = snapshotBits();
|
||||
const uint32_t u = [&] { uint32_t v = 0; for (int b = 0; b < 32; ++b) if (cur[b]) v |= (1u << b); return v; }();
|
||||
@@ -438,19 +444,20 @@ void main()
|
||||
signedTogglePtr->setOnToggleOffListener([&]() {
|
||||
gSigned = false;
|
||||
signedTogglePtr->setButtonText("无符号");
|
||||
|
||||
StellarX::MessageBox::showAsync(mainWindow, "无符号模式下,\n所有位均为数值位。", "无符号模式");
|
||||
auto cur = snapshotBits();
|
||||
const uint32_t u = [&] { uint32_t v = 0; for (int b = 0; b < 32; ++b) if (cur[b]) v |= (1u << b); return v; }();
|
||||
dec->setText(std::to_string(u));
|
||||
});
|
||||
|
||||
signedTogglePtr->enableTooltip(true);
|
||||
signedTogglePtr->setTooltipTextsForToggle("切换无符号模式", "切换有符号模式");
|
||||
|
||||
configuration->addControl(std::move(configurationButton[0]));
|
||||
configuration->addControl(std::move(configurationButton[1]));
|
||||
configuration->addControl(std::move(signedToggle));
|
||||
configuration->addControl(std::move(configurationLabel));
|
||||
|
||||
|
||||
mainWindow.addControl(std::move(selectionArea));
|
||||
mainWindow.addControl(std::move(function));
|
||||
mainWindow.addControl(std::move(NumericalDisplayArea));
|
||||
@@ -459,4 +466,4 @@ void main()
|
||||
|
||||
mainWindow.draw();
|
||||
return mainWindow.runEventLoop();
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
@@ -18,6 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "Control.h"
|
||||
#include"Table.h"
|
||||
|
||||
class Canvas : public Control
|
||||
{
|
||||
@@ -39,6 +40,7 @@ public:
|
||||
Canvas();
|
||||
Canvas(int x, int y, int width, int height);
|
||||
~Canvas() {}
|
||||
|
||||
//绘制容器及其子控件
|
||||
void draw() override;
|
||||
bool handleEvent(const ExMessage& msg) override;
|
||||
|
||||
@@ -9,23 +9,20 @@
|
||||
* - 定义控件基本属性(坐标、尺寸、脏标记)
|
||||
* - 提供绘图状态管理(saveStyle/restoreStyle)
|
||||
* - 声明纯虚接口(draw、handleEvent等)
|
||||
* - 支持移动语义,禁止拷贝语义
|
||||
* - 禁止移动语义,禁止拷贝语义
|
||||
*
|
||||
* @使用场景: 作为所有具体控件类的基类,不直接实例化
|
||||
* @所属框架: 星垣(StellarX) GUI框架
|
||||
* @作者: 我在人间做废物
|
||||
******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#endif
|
||||
#ifndef WINVER
|
||||
#define WINVER _WIN32_WINNT
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <easyx.h>
|
||||
@@ -45,6 +42,11 @@ protected:
|
||||
bool dirty = true; // 是否重绘
|
||||
bool show = true; // 是否显示
|
||||
|
||||
/* == 布局模式 == */
|
||||
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
||||
StellarX::Anchor anchor_1 = StellarX::Anchor::Top; // 锚点
|
||||
StellarX::Anchor anchor_2 = StellarX::Anchor::Left; // 锚点
|
||||
|
||||
/* == 背景快照 == */
|
||||
IMAGE* saveBkImage = nullptr;
|
||||
int saveBkX = 0, saveBkY = 0; // 快照保存起始坐标
|
||||
@@ -118,8 +120,8 @@ public:
|
||||
|
||||
void setX(int x) { this->x = x; dirty = true; }
|
||||
void setY(int y) { this->y = y; dirty = true; }
|
||||
void setWidth(int width) { this->width = width; dirty = true; }
|
||||
void setHeight(int height) { this->height = height; dirty = true; }
|
||||
virtual void setWidth(int width) { this->width = width; dirty = true; }
|
||||
virtual void setHeight(int height) { this->height = height; dirty = true; }
|
||||
public:
|
||||
|
||||
virtual void draw() = 0;
|
||||
@@ -130,7 +132,6 @@ public:
|
||||
void setParent(Control* parent) { this->parent = parent; }
|
||||
//设置是否重绘
|
||||
virtual void setDirty(bool dirty) { this->dirty = dirty; }
|
||||
|
||||
//检查控件是否可见
|
||||
bool IsVisible() const { return show; };
|
||||
//获取控件id
|
||||
@@ -139,6 +140,12 @@ public:
|
||||
bool isDirty() { return dirty; }
|
||||
//用来检查对话框是否模态,其他控件不用实现
|
||||
virtual bool model()const = 0;
|
||||
//布局
|
||||
void setLayoutMode(StellarX::LayoutMode layoutMode_);
|
||||
void steAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2);
|
||||
StellarX::Anchor getAnchor_1() const;
|
||||
StellarX::Anchor getAnchor_2() const;
|
||||
StellarX::LayoutMode getLayoutMode() const;
|
||||
protected:
|
||||
void saveStyle();
|
||||
void restoreStyle();
|
||||
|
||||
@@ -343,4 +343,47 @@ namespace StellarX
|
||||
Left,
|
||||
Right
|
||||
};
|
||||
/*
|
||||
* @枚举名称: LayoutMode
|
||||
* @功能描述: 定义了两种布局模式
|
||||
*
|
||||
* @详细说明:
|
||||
* 根据不同模式,在窗口拉伸时采用不同的布局策略
|
||||
*
|
||||
* @成员说明:
|
||||
* Fixed, - 固定布局
|
||||
* AnchorToEdges - 锚定布局
|
||||
*
|
||||
* @使用示例:
|
||||
* LayoutMode mode = LayoutMode::Fixed;
|
||||
*/
|
||||
enum class LayoutMode
|
||||
{
|
||||
Fixed,
|
||||
AnchorToEdges
|
||||
};
|
||||
/*
|
||||
* @枚举名称: Anchor
|
||||
* @功能描述: 定义了控件相对于窗口锚定的位置
|
||||
*
|
||||
* @详细说明:
|
||||
* 根据不同的锚定位置,有不同的拉伸策略
|
||||
*
|
||||
* @成员说明:
|
||||
* Top, - 锚定上边,控件上边与窗口上侧距离保持不变
|
||||
* Bottom, - 锚定底边,控件底边与窗口底边距离保持不变
|
||||
* Left, - 锚定左边,控件左边与窗口左侧距离保持不变
|
||||
* Right - 锚定右边,控件上边与窗口右侧距离保持不变
|
||||
*
|
||||
* @使用示例:
|
||||
* Anchor a = Anchor::Top;
|
||||
*/
|
||||
enum class Anchor
|
||||
{
|
||||
NoAnchor = 0,
|
||||
Left = 1,
|
||||
Right,
|
||||
Top,
|
||||
Bottom
|
||||
};
|
||||
}
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
void saveBackground(int x, int y, int w, int h)override;
|
||||
|
||||
void restBackground()override;
|
||||
|
||||
void addControl(std::unique_ptr<Control> control);
|
||||
|
||||
// 清除所有控件
|
||||
void clearControls();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* @文件: StellarX.h
|
||||
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
|
||||
* @版本: v2.2.1
|
||||
* @版本: v2.3.0
|
||||
* @描述:
|
||||
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
|
||||
* 基于EasyX图形库,提供简洁易用的API和丰富的控件。
|
||||
|
||||
@@ -64,5 +64,6 @@ public:
|
||||
int indexOf(const std::string& tabText) const;
|
||||
void setDirty(bool dirty) override;
|
||||
void requestRepaint(Control* parent)override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ private:
|
||||
bool isShowPageButton = true; // 是否显示翻页按钮
|
||||
bool isNeedDrawHeaders = true; // 是否需要绘制表头
|
||||
bool isNeedCellSize = true; // 是否需要计算单元格尺寸
|
||||
bool isNeedButtonAndPageNum = true; // 是否需要计算翻页按钮和页码信息
|
||||
|
||||
Button* prevButton = nullptr; // 上一页按钮
|
||||
Button* nextButton = nullptr; // 下一页按钮
|
||||
@@ -98,7 +99,8 @@ private:
|
||||
bool model() const override { return false; };
|
||||
public:
|
||||
StellarX::ControlText textStyle; // 文本样式
|
||||
|
||||
void setWidth(int width) override;
|
||||
void setHeight(int height) override;
|
||||
public:
|
||||
Table(int x, int y);
|
||||
~Table();
|
||||
@@ -127,6 +129,8 @@ public:
|
||||
void setTableLineStyle(StellarX::LineStyle style);
|
||||
//设置边框宽度
|
||||
void setTableBorderWidth(int width);
|
||||
//窗口变化丢快照+标脏
|
||||
void onWindowResize() override;
|
||||
|
||||
//************************** 获取属性 *****************************/
|
||||
|
||||
@@ -152,6 +156,9 @@ public:
|
||||
std::vector<std::vector<std::string>> getData() const;
|
||||
//获取表格边框宽度
|
||||
int getTableBorderWidth() const;
|
||||
//获取表格尺寸
|
||||
int getTableWidth() const;
|
||||
int getTableHeight() const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
+97
-73
@@ -1,86 +1,110 @@
|
||||
|
||||
/**
|
||||
* Window(头文件)
|
||||
*
|
||||
* 设计目标:
|
||||
* - 提供一个基于 Win32 + EasyX 的“可拉伸且稳定不抖”的窗口容器。
|
||||
* - 通过消息过程子类化(WndProcThunk)接管关键消息(WM_SIZING/WM_SIZE/...)。
|
||||
* - 将“几何变化记录(pendingW/H)”与“统一收口重绘(needResizeDirty)”解耦。
|
||||
*
|
||||
* 关键点(与 .cpp 中实现对应):
|
||||
* - isSizing:处于交互拉伸阶段时,冻结重绘;松手后统一重绘,防止抖动。
|
||||
* - WM_SIZING:只做“最小尺寸夹紧”,不回滚矩形、不做对齐;把其余交给系统。
|
||||
* - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。
|
||||
* - runEventLoop:只记录 WM_SIZE 的新尺寸;真正绘制放在 needResizeDirty 时集中处理。
|
||||
*/
|
||||
#pragma once
|
||||
#include"Control.h"
|
||||
/*******************************************************************************
|
||||
* @类: Window
|
||||
* @摘要: 应用程序主窗口类,管理窗口生命周期和消息循环
|
||||
* @描述:
|
||||
* 创建和管理应用程序主窗口,作为所有控件的根容器。
|
||||
* 处理消息分发、事件循环和渲染调度。
|
||||
*
|
||||
* @特性:
|
||||
* - 多种窗口模式配置(双缓冲、控制台等)
|
||||
* - 背景图片和颜色支持
|
||||
* - 集成的对话框管理系统
|
||||
* - 完整的消息处理循环
|
||||
* - 控件和对话框的生命周期管理
|
||||
*
|
||||
* @使用场景: 应用程序主窗口,GUI程序的入口和核心
|
||||
* @所属框架: 星垣(StellarX) GUI框架
|
||||
* @作者: 我在人间做废物
|
||||
******************************************************************************/
|
||||
|
||||
#include "Control.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <windows.h>
|
||||
|
||||
class Window
|
||||
{
|
||||
int width; //窗口宽度
|
||||
int height; //窗口高度
|
||||
int windowMode = NULL; //窗口模式
|
||||
// —— 尺寸状态 ——(绘制尺寸与待应用尺寸分离;收口时一次性更新)
|
||||
int width; // 当前有效宽(已应用到画布/控件的客户区宽)
|
||||
int height; // 当前有效高(已应用到画布/控件的客户区高)
|
||||
int localwidth; // 基准宽(创建时的宽度)
|
||||
int localheight; // 基准高(创建是的高度)
|
||||
int pendingW; // 待应用宽(WM_SIZE/拉伸中记录用)
|
||||
int pendingH; // 待应用高
|
||||
int minClientW; // 业务设定的最小客户区宽(用于 GETMINMAXINFO 与 SIZING 夹紧)
|
||||
int minClientH; // 业务设定的最小客户区高
|
||||
int windowMode = NULL; // EasyX 初始化模式(EX_SHOWCONSOLE/EX_TOPMOST/...)
|
||||
bool needResizeDirty = false; // 统一收口重绘标志(置位后在事件环末尾处理)
|
||||
bool isSizing = false; // 是否处于拖拽阶段(ENTER/EXIT SIZEMOVE 切换)
|
||||
|
||||
// --- 尺寸变化去抖用 ---
|
||||
int pendingW;
|
||||
int pendingH;
|
||||
bool needResizeDirty = false;
|
||||
// —— 原生窗口句柄与子类化钩子 ——(子类化 EasyX 的窗口过程以拦截关键消息)
|
||||
HWND hWnd = NULL; // EasyX 初始化后的窗口句柄
|
||||
WNDPROC oldWndProc = nullptr; // 保存旧过程(CallWindowProc 回落)
|
||||
bool procHooked = false; // 避免重复子类化
|
||||
static LRESULT CALLBACK WndProcThunk(HWND h, UINT m, WPARAM w, LPARAM l); // 静态过程分发到 this
|
||||
|
||||
HWND hWnd = NULL; //窗口句柄
|
||||
std::string headline; //窗口标题
|
||||
COLORREF wBkcolor = BLACK; //窗口背景
|
||||
IMAGE* background = nullptr; //窗口背景图片
|
||||
std::string bkImageFile; //窗口背景图片文件名
|
||||
std::vector<std::unique_ptr<Control>> controls; //控件管理
|
||||
std::vector<std::unique_ptr<Control>> dialogs; //对话框管理
|
||||
// —— 绘制相关 ——(是否使用合成双缓冲、窗口标题、背景等)
|
||||
bool useComposited = true; // 是否启用 WS_EX_COMPOSITED(部分机器可能增加一帧观感延迟)
|
||||
std::string headline; // 窗口标题文本
|
||||
COLORREF wBkcolor = BLACK; // 纯色背景(无背景图时使用)
|
||||
IMAGE* background = nullptr; // 背景图对象指针(存在时优先绘制)
|
||||
std::string bkImageFile; // 背景图文件路径(loadimage 用)
|
||||
|
||||
// —— 控件/对话框 ——(容器内的普通控件与非模态对话框)
|
||||
std::vector<std::unique_ptr<Control>> controls;
|
||||
std::vector<std::unique_ptr<Control>> dialogs;
|
||||
|
||||
public:
|
||||
bool dialogClose = false; //是否有对话框关闭
|
||||
bool dialogClose = false; // 项目内使用的状态位
|
||||
|
||||
Window(int width, int height, int mode);
|
||||
Window(int width, int height, int mode, COLORREF bkcloc);
|
||||
Window(int width, int height, int mode , COLORREF bkcloc, std::string headline = "窗口");
|
||||
~Window();
|
||||
//绘制窗口
|
||||
void draw();
|
||||
void draw(std::string pImgFile);
|
||||
//事件循环
|
||||
int runEventLoop();
|
||||
//设置窗口背景图片
|
||||
void setBkImage(std::string pImgFile);
|
||||
//设置窗口背景颜色
|
||||
void setBkcolor(COLORREF c);
|
||||
//设置窗口标题
|
||||
void setHeadline(std::string headline);
|
||||
//添加控件
|
||||
void addControl(std::unique_ptr<Control> control);
|
||||
//添加对话框
|
||||
void addDialog(std::unique_ptr<Control> dialogs);
|
||||
//检查是否已有对话框显示用于去重,防止工厂模式调用非模态对话框,多次打开污染对话框背景快照
|
||||
bool hasNonModalDialogWithCaption(const std::string& caption, const std::string& message) const;
|
||||
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
||||
Window(int width, int height, int mode);
|
||||
Window(int width, int height, int mode, COLORREF bkcloc);
|
||||
Window(int width, int height, int mode, COLORREF bkcloc, std::string headline);
|
||||
~Window();
|
||||
|
||||
// —— 绘制与事件循环 ——(draw* 完成一次全量绘制;runEventLoop 驱动事件与统一收口)
|
||||
void draw(); // 纯色背景版本
|
||||
void draw(std::string pImgFile); // 背景图版本
|
||||
int runEventLoop(); // 主事件循环(PeekMessage + 统一收口重绘)
|
||||
|
||||
//获取窗口句柄
|
||||
HWND getHwnd() const;
|
||||
//获取窗口宽度
|
||||
int getWidth() const;
|
||||
//获取窗口高度
|
||||
int getHeight() const;
|
||||
//获取窗口标题
|
||||
std::string getHeadline() const;
|
||||
//获取窗口背景颜色
|
||||
COLORREF getBkcolor() const;
|
||||
//获取窗口背景图片
|
||||
IMAGE* getBkImage() const;
|
||||
//获取窗口背景图片文件名
|
||||
std::string getBkImageFile() const;
|
||||
//获取控件管理
|
||||
std::vector<std::unique_ptr<Control>>& getControls();
|
||||
// —— 背景/标题设置 ——(更换背景、背景色与标题;立即触发一次批量绘制)
|
||||
void setBkImage(std::string pImgFile);
|
||||
void setBkcolor(COLORREF c);
|
||||
void setHeadline(std::string headline);
|
||||
|
||||
// —— 控件/对话框管理 ——(添加到容器,或做存在性判断)
|
||||
void addControl(std::unique_ptr<Control> control);
|
||||
void addDialog(std::unique_ptr<Control> dialogs);
|
||||
bool hasNonModalDialogWithCaption(const std::string& caption, const std::string& message) const;
|
||||
|
||||
// —— 访问器 ——(只读接口,供外部查询当前窗口/标题/背景等)
|
||||
HWND getHwnd() const;
|
||||
int getWidth() const;
|
||||
int getHeight() const;
|
||||
std::string getHeadline() const;
|
||||
COLORREF getBkcolor() const;
|
||||
IMAGE* getBkImage() const;
|
||||
std::string getBkImageFile() const;
|
||||
std::vector<std::unique_ptr<Control>>& getControls();
|
||||
|
||||
// —— 配置开关 ——(动态调整最小客户区、合成双缓冲)
|
||||
inline void setMinClientSize(int w, int h)
|
||||
{
|
||||
// 仅更新阈值;实际约束在 WM_GETMINMAXINFO/WM_SIZING 中生效
|
||||
minClientW = w;
|
||||
minClientH = h;
|
||||
}
|
||||
|
||||
inline void setComposited(bool on)
|
||||
{
|
||||
// 更新标志;真正应用在 draw()/样式 SetWindowLongEx + SWP_FRAMECHANGED
|
||||
useComposited = on;
|
||||
}
|
||||
|
||||
void processWindowMessage(const ExMessage & msg); // 处理 EX_WINDOW 中的 WM_SIZE 等
|
||||
void pumpResizeIfNeeded(); // 执行一次统一收口重绘
|
||||
void scheduleResizeFromModal(int w, int h);
|
||||
private:
|
||||
void adaptiveLayout(std::unique_ptr<Control>& c,const int finalH, const int finalW);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
+66
-66
@@ -22,7 +22,7 @@ static inline int gbk_char_len(const std::string& s, size_t i)
|
||||
{
|
||||
unsigned char b = (unsigned char)s[i];
|
||||
if (b <= 0x7F) return 1; // ASCII
|
||||
if (b >= 0x81 && b <= 0xFE && i + 1 < s.size())
|
||||
if (b >= 0x81 && b <= 0xFE && i + 1 < s.size())
|
||||
{
|
||||
unsigned char b2 = (unsigned char)s[i + 1];
|
||||
if (b2 >= 0x40 && b2 <= 0xFE && b2 != 0x7F) return 2; // 合法双字节
|
||||
@@ -30,10 +30,10 @@ static inline int gbk_char_len(const std::string& s, size_t i)
|
||||
return 1; // 容错
|
||||
}
|
||||
|
||||
static inline void rtrim_spaces_gbk(std::string& s)
|
||||
static inline void rtrim_spaces_gbk(std::string& s)
|
||||
{
|
||||
while (!s.empty() && s.back() == ' ') s.pop_back(); // ASCII 空格
|
||||
while (s.size() >= 2)
|
||||
while (s.size() >= 2)
|
||||
{ // 全角空格 A1 A1
|
||||
unsigned char a = (unsigned char)s[s.size() - 2];
|
||||
unsigned char b = (unsigned char)s[s.size() - 1];
|
||||
@@ -42,26 +42,26 @@ static inline void rtrim_spaces_gbk(std::string& s)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_ascii_only(const std::string& s)
|
||||
static inline bool is_ascii_only(const std::string& s)
|
||||
{
|
||||
for (unsigned char c : s) if (c > 0x7F) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_word_boundary_char(unsigned char c)
|
||||
static inline bool is_word_boundary_char(unsigned char c)
|
||||
{
|
||||
return c == ' ' || c == '-' || c == '_' || c == '/' || c == '\\' || c == '.' || c == ':';
|
||||
}
|
||||
|
||||
// 英文优先策略:优先在“词边界”回退,再退化到逐字符;省略号为 "..."
|
||||
static std::string ellipsize_ascii_pref(const std::string& text, int maxW)
|
||||
static std::string ellipsize_ascii_pref(const std::string& text, int maxW)
|
||||
{
|
||||
if (maxW <= 0) return "";
|
||||
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
||||
|
||||
const std::string ell = "...";
|
||||
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
||||
if (ellW > maxW)
|
||||
if (ellW > maxW)
|
||||
{ // 连 ... 都放不下
|
||||
std::string e = ell;
|
||||
while (!e.empty() && textwidth(LPCTSTR(e.c_str())) > maxW) e.pop_back();
|
||||
@@ -71,7 +71,7 @@ static std::string ellipsize_ascii_pref(const std::string& text, int maxW)
|
||||
|
||||
// 先找到能放下的最长前缀
|
||||
size_t i = 0, lastFit = 0;
|
||||
while (i < text.size())
|
||||
while (i < text.size())
|
||||
{
|
||||
int clen = gbk_char_len(text, i);
|
||||
size_t j = text.size() < i + (size_t)clen ? text.size() : i + (size_t)clen;
|
||||
@@ -83,7 +83,7 @@ static std::string ellipsize_ascii_pref(const std::string& text, int maxW)
|
||||
|
||||
// 在已适配前缀范围内,向左找最近的词边界
|
||||
size_t cutPos = lastFit;
|
||||
for (size_t k = lastFit; k > 0; --k)
|
||||
for (size_t k = lastFit; k > 0; --k)
|
||||
{
|
||||
unsigned char c = (unsigned char)text[k - 1];
|
||||
if (c <= 0x7F && is_word_boundary_char(c)) { cutPos = k - 1; break; }
|
||||
@@ -96,14 +96,14 @@ static std::string ellipsize_ascii_pref(const std::string& text, int maxW)
|
||||
}
|
||||
|
||||
// 中文优先策略:严格逐“字符”(1/2字节)回退;省略号用全角 "…"
|
||||
static std::string ellipsize_cjk_pref(const std::string& text, int maxW, const char* ellipsis = "…")
|
||||
static std::string ellipsize_cjk_pref(const std::string& text, int maxW, const char* ellipsis = "…")
|
||||
{
|
||||
if (maxW <= 0) return "";
|
||||
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
||||
|
||||
std::string ell = ellipsis ? ellipsis : "…";
|
||||
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
||||
if (ellW > maxW)
|
||||
if (ellW > maxW)
|
||||
{ // 连省略号都放不下
|
||||
std::string e = ell;
|
||||
while (!e.empty() && textwidth(LPCTSTR(e.c_str())) > maxW) e.pop_back();
|
||||
@@ -112,7 +112,7 @@ static std::string ellipsize_cjk_pref(const std::string& text, int maxW, const c
|
||||
const int limit = maxW - ellW;
|
||||
|
||||
size_t i = 0, lastFit = 0;
|
||||
while (i < text.size())
|
||||
while (i < text.size())
|
||||
{
|
||||
int clen = gbk_char_len(text, i);
|
||||
size_t j = text.size() < i + (size_t)clen ? text.size() : i + (size_t)clen;
|
||||
@@ -166,8 +166,8 @@ void Button::initButton(const std::string text, StellarX::ButtonMode mode, Stell
|
||||
|
||||
Button::~Button()
|
||||
{
|
||||
if (buttonFileIMAGE)
|
||||
delete buttonFileIMAGE;
|
||||
if (buttonFileIMAGE)
|
||||
delete buttonFileIMAGE;
|
||||
buttonFileIMAGE = nullptr;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
||||
// 处理鼠标点击事件
|
||||
if (msg.message == WM_LBUTTONDOWN && hover && mode != StellarX::ButtonMode::DISABLED)
|
||||
{
|
||||
|
||||
|
||||
if (mode == StellarX::ButtonMode::NORMAL)
|
||||
{
|
||||
click = true;
|
||||
@@ -327,7 +327,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
||||
}
|
||||
}
|
||||
// NORMAL 模式:鼠标在按钮上释放时才触发点击回调,如果移出区域则取消点击状态。
|
||||
// TOGGLE 模式:在释放时切换状态,并触发相应的开/关回调。
|
||||
// TOGGLE 模式:在释放时切换状态,并触发相应的开/关回调。
|
||||
else if (msg.message == WM_LBUTTONUP && hover && mode != StellarX::ButtonMode::DISABLED)
|
||||
{
|
||||
hideTooltip(); // 隐藏悬停提示
|
||||
@@ -349,7 +349,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
||||
dirty = true;
|
||||
consume = true;
|
||||
refreshTooltipTextForState();
|
||||
hideTooltip();
|
||||
hideTooltip();
|
||||
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
||||
flushmessage(EX_MOUSE | EX_KEY);
|
||||
}
|
||||
@@ -367,23 +367,23 @@ bool Button::handleEvent(const ExMessage& msg)
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (tipEnabled)
|
||||
if (tipEnabled)
|
||||
{
|
||||
if (hover && !oldHover)
|
||||
if (hover && !oldHover)
|
||||
{
|
||||
// 刚刚进入悬停:开计时,暂不显示
|
||||
tipHoverTick = GetTickCount64();
|
||||
tipVisible = false;
|
||||
}
|
||||
if (!hover && oldHover)
|
||||
if (!hover && oldHover)
|
||||
{
|
||||
// 刚移出:立即隐藏
|
||||
hideTooltip();
|
||||
}
|
||||
if (hover && !tipVisible)
|
||||
if (hover && !tipVisible)
|
||||
{
|
||||
// 到点就显示
|
||||
if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs)
|
||||
if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs)
|
||||
{
|
||||
tipVisible = true;
|
||||
|
||||
@@ -420,18 +420,18 @@ bool Button::handleEvent(const ExMessage& msg)
|
||||
|
||||
if (tipEnabled && tipVisible)
|
||||
tipLabel.draw();
|
||||
|
||||
|
||||
return consume;
|
||||
}
|
||||
|
||||
void Button::setOnClickListener(const std::function<void()>&& callback)
|
||||
{
|
||||
this->onClickCallback = callback;
|
||||
this->onClickCallback = callback;
|
||||
}
|
||||
|
||||
void Button::setOnToggleOnListener(const std::function<void()>&& callback)
|
||||
{
|
||||
this->onToggleOnCallback = callback;
|
||||
this->onToggleOnCallback = callback;
|
||||
}
|
||||
void Button::setOnToggleOffListener(const std::function<void()>&& callback)
|
||||
{
|
||||
@@ -443,37 +443,37 @@ void Button::setbuttonMode(StellarX::ButtonMode mode)
|
||||
if (this->mode == StellarX::ButtonMode::DISABLED && mode != StellarX::ButtonMode::DISABLED)
|
||||
textStyle.bStrikeOut = false;
|
||||
//取值范围参考 buttMode的枚举注释
|
||||
this->mode = mode;
|
||||
this->mode = mode;
|
||||
dirty = true; // 标记需要重绘
|
||||
}
|
||||
|
||||
void Button::setROUND_RECTANGLEwidth(int width)
|
||||
{
|
||||
rouRectangleSize.ROUND_RECTANGLEwidth = width;
|
||||
rouRectangleSize.ROUND_RECTANGLEwidth = width;
|
||||
this->dirty = true; // 标记需要重绘
|
||||
|
||||
}
|
||||
|
||||
void Button::setROUND_RECTANGLEheight(int height)
|
||||
{
|
||||
rouRectangleSize.ROUND_RECTANGLEheight = height;
|
||||
rouRectangleSize.ROUND_RECTANGLEheight = height;
|
||||
this->dirty = true; // 标记需要重绘
|
||||
}
|
||||
|
||||
bool Button::isClicked() const
|
||||
{
|
||||
return this->click;
|
||||
return this->click;
|
||||
}
|
||||
|
||||
void Button::setFillMode(StellarX::FillMode mode)
|
||||
{
|
||||
this->buttonFillMode = mode;
|
||||
this->buttonFillMode = mode;
|
||||
this->dirty = true; // 标记需要重绘
|
||||
}
|
||||
|
||||
void Button::setFillIma(StellarX::FillStyle ima)
|
||||
{
|
||||
buttonFillIma = ima;
|
||||
buttonFillIma = ima;
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
@@ -484,8 +484,8 @@ void Button::setFillIma(std::string imaNAme)
|
||||
delete buttonFileIMAGE;
|
||||
buttonFileIMAGE = nullptr;
|
||||
}
|
||||
buttonFileIMAGE = new IMAGE;
|
||||
loadimage(buttonFileIMAGE, imaNAme.c_str(),width,height);
|
||||
buttonFileIMAGE = new IMAGE;
|
||||
loadimage(buttonFileIMAGE, imaNAme.c_str(), width, height);
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ void Button::setFillIma(std::string imaNAme)
|
||||
void Button::setButtonBorder(COLORREF Border)
|
||||
{
|
||||
buttonBorderColor = Border;
|
||||
this->dirty = true;
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
void Button::setButtonFalseColor(COLORREF color)
|
||||
@@ -504,7 +504,7 @@ void Button::setButtonFalseColor(COLORREF color)
|
||||
|
||||
void Button::setButtonText(const char* text)
|
||||
{
|
||||
this->text = std::string(text);
|
||||
this->text = std::string(text);
|
||||
this->text_width = textwidth(LPCTSTR(this->text.c_str()));
|
||||
this->text_height = textheight(LPCTSTR(this->text.c_str()));
|
||||
this->dirty = true;
|
||||
@@ -526,7 +526,7 @@ void Button::setButtonText(std::string text)
|
||||
|
||||
void Button::setButtonShape(StellarX::ControlShape shape)
|
||||
{
|
||||
this->shape = shape;
|
||||
this->shape = shape;
|
||||
this->dirty = true;
|
||||
this->needCutText = true;
|
||||
}
|
||||
@@ -535,7 +535,7 @@ void Button::setButtonShape(StellarX::ControlShape shape)
|
||||
void Button::setButtonClick(BOOL click)
|
||||
{
|
||||
this->click = click;
|
||||
|
||||
|
||||
if (mode == StellarX::ButtonMode::NORMAL && click)
|
||||
{
|
||||
if (onClickCallback) onClickCallback();
|
||||
@@ -550,7 +550,7 @@ void Button::setButtonClick(BOOL click)
|
||||
else if (!click && onToggleOffCallback) onToggleOffCallback();
|
||||
dirty = true;
|
||||
refreshTooltipTextForState();
|
||||
hideTooltip();
|
||||
hideTooltip();
|
||||
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
||||
flushmessage(EX_MOUSE | EX_KEY);
|
||||
}
|
||||
@@ -561,52 +561,52 @@ void Button::setButtonClick(BOOL click)
|
||||
|
||||
std::string Button::getButtonText() const
|
||||
{
|
||||
return this->text;
|
||||
return this->text;
|
||||
}
|
||||
|
||||
const char* Button::getButtonText_c() const
|
||||
{
|
||||
return this->text.c_str();
|
||||
return this->text.c_str();
|
||||
}
|
||||
|
||||
StellarX::ButtonMode Button::getButtonMode() const
|
||||
{
|
||||
return this->mode;
|
||||
return this->mode;
|
||||
}
|
||||
|
||||
StellarX::ControlShape Button::getButtonShape() const
|
||||
{
|
||||
return this->shape;
|
||||
return this->shape;
|
||||
}
|
||||
|
||||
StellarX::FillMode Button::getFillMode() const
|
||||
{
|
||||
return this->buttonFillMode;
|
||||
return this->buttonFillMode;
|
||||
}
|
||||
|
||||
StellarX::FillStyle Button::getFillIma() const
|
||||
{
|
||||
return this->buttonFillIma;
|
||||
return this->buttonFillIma;
|
||||
}
|
||||
|
||||
IMAGE* Button::getFillImaImage() const
|
||||
{
|
||||
return this->buttonFileIMAGE;
|
||||
return this->buttonFileIMAGE;
|
||||
}
|
||||
|
||||
COLORREF Button::getButtonBorder() const
|
||||
{
|
||||
return this->buttonBorderColor;
|
||||
return this->buttonBorderColor;
|
||||
}
|
||||
|
||||
COLORREF Button::getButtonTextColor() const
|
||||
{
|
||||
return this->textStyle.color;
|
||||
return this->textStyle.color;
|
||||
}
|
||||
|
||||
StellarX::ControlText Button::getButtonTextStyle() const
|
||||
{
|
||||
return this->textStyle;
|
||||
return this->textStyle;
|
||||
}
|
||||
|
||||
int Button::getButtonWidth() const
|
||||
@@ -623,11 +623,11 @@ int Button::getButtonHeight() const
|
||||
|
||||
bool Button::isMouseInCircle(int mouseX, int mouseY, int x, int y, int radius)
|
||||
{
|
||||
double dis = sqrt(pow(mouseX - x, 2) + pow(mouseY - y, 2));
|
||||
if (dis <= radius)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
double dis = sqrt(pow(mouseX - x, 2) + pow(mouseY - y, 2));
|
||||
if (dis <= radius)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, int height)
|
||||
@@ -636,15 +636,15 @@ bool Button::isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, i
|
||||
int centerY = (y + height) / 2;
|
||||
int majorAxis = (width - x) / 2;
|
||||
int minorAxis = (height - y) / 2;
|
||||
double dx = mouseX - centerX;
|
||||
double dy = mouseY - centerY;
|
||||
double normalizedDistance = (dx * dx) / (majorAxis * majorAxis) + (dy * dy) / (minorAxis * minorAxis);
|
||||
double dx = mouseX - centerX;
|
||||
double dy = mouseY - centerY;
|
||||
double normalizedDistance = (dx * dx) / (majorAxis * majorAxis) + (dy * dy) / (minorAxis * minorAxis);
|
||||
|
||||
// 判断鼠标是否在椭圆内
|
||||
if (normalizedDistance <= 1.0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
// 判断鼠标是否在椭圆内
|
||||
if (normalizedDistance <= 1.0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Button::cutButtonText()
|
||||
@@ -659,18 +659,18 @@ void Button::cutButtonText()
|
||||
}
|
||||
|
||||
// 放不下:按语言偏好裁切(ASCII→词边界;CJK→逐字符,不撕裂双字节)
|
||||
if (is_ascii_only(this->text))
|
||||
if (is_ascii_only(this->text))
|
||||
{
|
||||
cutText = ellipsize_ascii_pref(this->text, contentW); // "..."
|
||||
}
|
||||
else
|
||||
{
|
||||
cutText = ellipsize_cjk_pref(this->text, contentW, "…"); // 全角省略号
|
||||
|
||||
|
||||
}
|
||||
isUseCutText = true;
|
||||
needCutText = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Button::hideTooltip()
|
||||
@@ -686,9 +686,9 @@ void Button::hideTooltip()
|
||||
void Button::refreshTooltipTextForState()
|
||||
{
|
||||
if (tipUserOverride) return; // 用户显式设置过 tipText,保持不变
|
||||
if(mode==StellarX::ButtonMode::NORMAL)
|
||||
if (mode == StellarX::ButtonMode::NORMAL)
|
||||
tipLabel.setText(tipTextClick);
|
||||
else if(mode==StellarX::ButtonMode::TOGGLE)
|
||||
else if (mode == StellarX::ButtonMode::TOGGLE)
|
||||
tipLabel.setText(click ? tipTextOn : tipTextOff);
|
||||
}
|
||||
|
||||
|
||||
+200
-14
@@ -17,21 +17,43 @@ void Canvas::clearAllControls()
|
||||
controls.clear();
|
||||
}
|
||||
|
||||
|
||||
void Canvas::draw()
|
||||
{
|
||||
if (!dirty||!show)return;
|
||||
if (!dirty || !show)
|
||||
{
|
||||
for (auto& control : controls)
|
||||
if (auto c = dynamic_cast<Table*>(control.get()))
|
||||
c->draw();
|
||||
return;
|
||||
}
|
||||
saveStyle();
|
||||
|
||||
setlinecolor(canvasBorderClor);//设置线色
|
||||
setfillcolor(canvasBkClor);//设置填充色
|
||||
if(StellarX::FillMode::Null != canvasFillMode)
|
||||
setfillcolor(canvasBkClor);//设置填充色
|
||||
setfillstyle((int)canvasFillMode);//设置填充模式
|
||||
setlinestyle((int)canvasLineStyle, canvaslinewidth);
|
||||
|
||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
||||
saveBackground(x, y, width, height);
|
||||
// 恢复背景(清除旧内容)
|
||||
restBackground();
|
||||
// 在绘制画布之前,先恢复并更新背景快照:
|
||||
// 1. 如果已有快照,则先回贴旧快照以清除之前的内容。
|
||||
// 2. 当坐标或尺寸变化,或缓存图像无效时,丢弃旧快照并重新抓取新的背景。
|
||||
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();
|
||||
//根据画布形状绘制
|
||||
switch (shape)
|
||||
{
|
||||
@@ -76,6 +98,9 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
||||
|
||||
void Canvas::addControl(std::unique_ptr<Control> control)
|
||||
{
|
||||
//坐标转化
|
||||
control->setX(control->getLocalX() + this->x);
|
||||
control->setY(control->getLocalY() + this->y);
|
||||
control->setParent(this);
|
||||
controls.push_back(std::move(control));
|
||||
dirty = true;
|
||||
@@ -155,9 +180,173 @@ void Canvas::setDirty(bool dirty)
|
||||
|
||||
void Canvas::onWindowResize()
|
||||
{
|
||||
Control::onWindowResize(); // 先处理自己
|
||||
for (auto& ch : controls) // 再转发给所有子控件
|
||||
ch->onWindowResize();
|
||||
// 首先处理自身的快照等逻辑
|
||||
Control::onWindowResize();
|
||||
|
||||
// 记录父容器原始尺寸(用于计算子控件的右/下边距)
|
||||
int origParentW = this->localWidth;
|
||||
int origParentH = this->localHeight;
|
||||
|
||||
// 当前容器的新尺寸
|
||||
int finalW = this->width;
|
||||
int finalH = this->height;
|
||||
|
||||
// 当前容器的新坐标(全局坐标)
|
||||
int parentX = this->x;
|
||||
int parentY = this->y;
|
||||
|
||||
// 调整每个子控件在 AnchorToEdges 模式下的位置与尺寸
|
||||
for (auto& ch : controls)
|
||||
{
|
||||
// Only adjust when using anchor-to-edges layout
|
||||
if (ch->getLayoutMode() == StellarX::LayoutMode::AnchorToEdges)
|
||||
{
|
||||
// Determine whether this child is a Table; tables keep their height constant
|
||||
bool isTable = (dynamic_cast<Table*>(ch.get()) != nullptr);
|
||||
|
||||
// Unpack anchors
|
||||
auto a1 = ch->getAnchor_1();
|
||||
auto a2 = ch->getAnchor_2();
|
||||
|
||||
bool anchorLeft = (a1 == StellarX::Anchor::Left || a2 == StellarX::Anchor::Left);
|
||||
bool anchorRight = (a1 == StellarX::Anchor::Right || a2 == StellarX::Anchor::Right);
|
||||
bool anchorTop = (a1 == StellarX::Anchor::Top || a2 == StellarX::Anchor::Top);
|
||||
bool anchorBottom = (a1 == StellarX::Anchor::Bottom || a2 == StellarX::Anchor::Bottom);
|
||||
|
||||
// If it's a table, treat as anchored left and right horizontally and anchored top vertically by default.
|
||||
if (isTable)
|
||||
{
|
||||
anchorLeft = true;
|
||||
anchorRight = true;
|
||||
// If no explicit vertical anchor was provided, default to top.
|
||||
if (!(anchorTop || anchorBottom))
|
||||
{
|
||||
anchorTop = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute new X and width
|
||||
int newX = ch->getX();
|
||||
int newWidth = ch->getWidth();
|
||||
if (anchorLeft && anchorRight)
|
||||
{
|
||||
// Scale horizontally relative to parent's size.
|
||||
if (origParentW > 0)
|
||||
{
|
||||
// Maintain proportional position and size based on original local values.
|
||||
double scaleW = static_cast<double>(finalW) / static_cast<double>(origParentW);
|
||||
newX = parentX + static_cast<int>(ch->getLocalX() * scaleW + 0.5);
|
||||
newWidth = static_cast<int>(ch->getLocalWidth() * scaleW + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: keep original
|
||||
newX = parentX + ch->getLocalX();
|
||||
newWidth = ch->getLocalWidth();
|
||||
}
|
||||
}
|
||||
else if (anchorLeft && !anchorRight)
|
||||
{
|
||||
// Only left anchored: keep original width and left margin.
|
||||
newWidth = ch->getLocalWidth();
|
||||
newX = parentX + ch->getLocalX();
|
||||
}
|
||||
else if (!anchorLeft && anchorRight)
|
||||
{
|
||||
// Only right anchored: keep original width and right margin.
|
||||
newWidth = ch->getLocalWidth();
|
||||
int origRightDist = origParentW - (ch->getLocalX() + ch->getLocalWidth());
|
||||
newX = parentX + finalW - origRightDist - newWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No horizontal anchor: position relative to parent's left and width unchanged.
|
||||
newWidth = ch->getLocalWidth();
|
||||
newX = parentX + ch->getLocalX();
|
||||
}
|
||||
ch->setX(newX);
|
||||
ch->setWidth(newWidth);
|
||||
|
||||
// Compute new Y and height
|
||||
int newY = ch->getY();
|
||||
int newHeight = ch->getHeight();
|
||||
if (isTable)
|
||||
{
|
||||
// Table: Height remains constant; adjust Y based on anchors.
|
||||
newHeight = ch->getLocalHeight();
|
||||
if (anchorTop && anchorBottom)
|
||||
{
|
||||
// If both top and bottom anchored, scale Y but keep height.
|
||||
if (origParentH > 0)
|
||||
{
|
||||
double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
|
||||
newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
else if (anchorTop && !anchorBottom)
|
||||
{
|
||||
// Top anchored only
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
else if (!anchorTop && anchorBottom)
|
||||
{
|
||||
// Bottom anchored only
|
||||
int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
|
||||
newY = parentY + finalH - origBottomDist - newHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No vertical anchor: default to top
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anchorTop && anchorBottom)
|
||||
{
|
||||
// Scale vertically relative to parent's size.
|
||||
if (origParentH > 0)
|
||||
{
|
||||
double scaleH = static_cast<double>(finalH) / static_cast<double>(origParentH);
|
||||
newY = parentY + static_cast<int>(ch->getLocalY() * scaleH + 0.5);
|
||||
newHeight = static_cast<int>(ch->getLocalHeight() * scaleH + 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
newY = parentY + ch->getLocalY();
|
||||
newHeight = ch->getLocalHeight();
|
||||
}
|
||||
}
|
||||
else if (anchorTop && !anchorBottom)
|
||||
{
|
||||
// Top anchored only: keep height constant
|
||||
newHeight = ch->getLocalHeight();
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
else if (!anchorTop && anchorBottom)
|
||||
{
|
||||
// Bottom anchored only: keep height and adjust Y relative to bottom
|
||||
newHeight = ch->getLocalHeight();
|
||||
int origBottomDist = origParentH - (ch->getLocalY() + ch->getLocalHeight());
|
||||
newY = parentY + finalH - origBottomDist - newHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No vertical anchor: position relative to parent's top, height constant.
|
||||
newHeight = ch->getLocalHeight();
|
||||
newY = parentY + ch->getLocalY();
|
||||
}
|
||||
}
|
||||
ch->setY(newY);
|
||||
ch->setHeight(newHeight);
|
||||
}
|
||||
// Always forward the window resize event to the child (recursively).
|
||||
ch->onWindowResize();
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::requestRepaint(Control* parent)
|
||||
@@ -166,10 +355,7 @@ void Canvas::requestRepaint(Control* parent)
|
||||
{
|
||||
for (auto& control : controls)
|
||||
if (control->isDirty() && control->IsVisible())
|
||||
{
|
||||
control->draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
onRequestRepaintAsRoot();
|
||||
|
||||
@@ -54,6 +54,27 @@ void Control::onWindowResize()
|
||||
discardBackground();
|
||||
setDirty(true);
|
||||
}
|
||||
void Control::setLayoutMode(StellarX::LayoutMode layoutMode_)
|
||||
{
|
||||
this->layoutMode = layoutMode_;
|
||||
}
|
||||
void Control::steAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2)
|
||||
{
|
||||
this->anchor_1 = anchor_1;
|
||||
this->anchor_2 = anchor_2;
|
||||
}
|
||||
StellarX::Anchor Control::getAnchor_1() const
|
||||
{
|
||||
return this->anchor_1;
|
||||
}
|
||||
StellarX::Anchor Control::getAnchor_2() const
|
||||
{
|
||||
return this->anchor_2;
|
||||
}
|
||||
StellarX::LayoutMode Control::getLayoutMode() const
|
||||
{
|
||||
return this->layoutMode;
|
||||
}
|
||||
// 保存当前的绘图状态(字体、颜色、线型等)
|
||||
// 在控件绘制前调用,确保不会影响全局绘图状态
|
||||
void Control::saveStyle()
|
||||
|
||||
+85
-26
@@ -59,7 +59,7 @@ void Dialog::draw()
|
||||
|
||||
|
||||
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坐标
|
||||
outtextxy(tx, ty, LPCTSTR(line.c_str()));
|
||||
@@ -170,33 +170,71 @@ void Dialog::Show()
|
||||
if (modal)
|
||||
{
|
||||
// 模态对话框需要阻塞当前线程直到对话框关闭
|
||||
while (show && !close)
|
||||
if (modal)
|
||||
{
|
||||
|
||||
// 处理消息
|
||||
ExMessage msg;
|
||||
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
|
||||
{
|
||||
handleEvent(msg);
|
||||
// 记录当前窗口客户区尺寸,供轮询对比
|
||||
RECT rc0;
|
||||
GetClientRect(hWnd.getHwnd(), &rc0);
|
||||
int lastW = rc0.right - rc0.left;
|
||||
int lastH = rc0.bottom - rc0.top;
|
||||
|
||||
// 检查是否需要关闭
|
||||
if (shouldClose)
|
||||
while (show && !close)
|
||||
{
|
||||
// ① 轮询窗口尺寸(不依赖 WM_SIZE)
|
||||
RECT rc;
|
||||
GetClientRect(hWnd.getHwnd(), &rc);
|
||||
const int cw = rc.right - rc.left;
|
||||
const int ch = rc.bottom - rc.top;
|
||||
|
||||
if (cw != lastW || ch != lastH)
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
lastW = cw;
|
||||
lastH = ch;
|
||||
|
||||
// 通知父窗口:有新尺寸 → 标记 needResizeDirty
|
||||
hWnd.scheduleResizeFromModal(cw, ch);
|
||||
|
||||
// 立即统一收口:父窗重绘 背景+普通控件(不会画到这只模态)
|
||||
hWnd.pumpResizeIfNeeded();
|
||||
|
||||
// 这只模态在新尺寸下重建布局 / 重抓背景 → 本帧要画自己
|
||||
setInitialization(true);
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
// ② 处理这只对话框的鼠标/键盘(沿用你原来 EX_MOUSE | EX_KEY)
|
||||
ExMessage msg;
|
||||
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
|
||||
{
|
||||
handleEvent(msg);
|
||||
if (shouldClose)
|
||||
{
|
||||
Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ③ 最后一笔:只画这只模态,保证永远在最上层
|
||||
if (dirty)
|
||||
{
|
||||
BeginBatchDraw();
|
||||
this->draw(); // 注意:不要 requestRepaint(parent),只画自己
|
||||
EndBatchDraw();
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
// 重绘
|
||||
if (dirty)
|
||||
{
|
||||
requestRepaint(parent);
|
||||
FlushBatchDraw();
|
||||
}
|
||||
|
||||
// 避免CPU占用过高
|
||||
Sleep(10);
|
||||
if (pendingCleanup && !isCleaning)
|
||||
performDelayedCleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 非模态仍由主循环托管
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// 模态对话框关闭后执行清理
|
||||
if (pendingCleanup && !isCleaning)
|
||||
performDelayedCleanup();
|
||||
@@ -517,7 +555,7 @@ void Dialog::getTextSize()
|
||||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
||||
for (auto text : lines)
|
||||
for (auto& text : lines)
|
||||
{
|
||||
int w = textwidth(LPCTSTR(text.c_str()));
|
||||
int h = textheight(LPCTSTR(text.c_str()));
|
||||
@@ -609,6 +647,13 @@ void Dialog::restBackground()
|
||||
putimage(saveBkX - BorderWidth, saveBkY - BorderWidth,saveBkImage);
|
||||
}
|
||||
|
||||
void Dialog::addControl(std::unique_ptr<Control> control)
|
||||
{
|
||||
control->setParent(this);
|
||||
controls.push_back(std::move(control));
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
// 延迟清理策略:由于对话框绘制时保存了背景快照,必须在对话框隐藏后、
|
||||
// 所有控件析构前恢复背景,否则会导致背景图像被错误覆盖。
|
||||
// 此方法在对话框不可见且被标记为待清理时由 draw() 或 handleEvent() 调用。
|
||||
@@ -634,6 +679,23 @@ void Dialog::performDelayedCleanup()
|
||||
FlushBatchDraw();
|
||||
discardBackground();
|
||||
}
|
||||
if (!(saveBkImage && hasSnap))
|
||||
{
|
||||
// 没有背景快照:强制一次完整重绘,立即擦掉残影
|
||||
hWnd.pumpResizeIfNeeded(); // 如果正好有尺寸标志,顺便统一收口
|
||||
// 即使没有尺寸变化,也重绘一帧
|
||||
BeginBatchDraw();
|
||||
// 背景
|
||||
if (hWnd.getBkImage() && !hWnd.getBkImageFile().empty())
|
||||
putimage(0, 0, hWnd.getBkImage());
|
||||
else { setbkcolor(hWnd.getBkcolor()); cleardevice(); }
|
||||
// 所有普通控件
|
||||
for (auto& c : hWnd.getControls()) c->draw();
|
||||
// 其他对话框(this 已经 show=false,会早退不绘)
|
||||
// 注意:此处若有容器管理,需要按你的现状遍历 dialogs 再 draw
|
||||
EndBatchDraw();
|
||||
FlushBatchDraw();
|
||||
}
|
||||
// 重置状态
|
||||
needsInitialization = true;
|
||||
pendingCleanup = false;
|
||||
@@ -684,11 +746,8 @@ void Dialog::requestRepaint(Control* parent)
|
||||
if (this == parent)
|
||||
{
|
||||
for (auto& control : controls)
|
||||
if (control->isDirty()&&control->IsVisible())
|
||||
{
|
||||
if (control->isDirty() && control->IsVisible())
|
||||
control->draw();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
|
||||
+39
-22
@@ -165,22 +165,37 @@ TabControl::~TabControl()
|
||||
void TabControl::draw()
|
||||
{
|
||||
if (!dirty || !show)return;
|
||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
||||
saveBackground(this->x, this->y, this->width, this->height);
|
||||
// 恢复背景(清除旧内容)
|
||||
restBackground();
|
||||
Canvas::draw();
|
||||
// 在绘制 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();
|
||||
for (auto& c : controls)
|
||||
{
|
||||
c.first->setDirty(true);
|
||||
c.first->draw();
|
||||
}
|
||||
for (auto& c : controls)
|
||||
if(c.second->IsVisible())
|
||||
{
|
||||
c.second->setDirty(true);
|
||||
c.second->draw();
|
||||
}
|
||||
{
|
||||
c.second->setDirty(true);
|
||||
c.second->draw();
|
||||
}
|
||||
dirty = false;
|
||||
|
||||
}
|
||||
@@ -216,6 +231,7 @@ void TabControl::add(std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>
|
||||
controls[idx].first->setParent(this);
|
||||
controls[idx].first->enableTooltip(true);
|
||||
controls[idx].first->setbuttonMode(StellarX::ButtonMode::TOGGLE);
|
||||
|
||||
controls[idx].first->setOnToggleOnListener([this,idx]()
|
||||
{
|
||||
controls[idx].second->setIsVisible(true);
|
||||
@@ -291,12 +307,19 @@ void TabControl::setIsVisible(bool visible)
|
||||
|
||||
void TabControl::onWindowResize()
|
||||
{
|
||||
Control::onWindowResize();
|
||||
for (auto& c : controls)
|
||||
{
|
||||
c.first->onWindowResize();
|
||||
c.second->onWindowResize();
|
||||
}
|
||||
// 调用基类的窗口变化处理,丢弃快照并标记脏
|
||||
Control::onWindowResize();
|
||||
// 根据当前 TabControl 的新尺寸重新计算页签栏和页面区域
|
||||
initTabBar();
|
||||
initTabPage();
|
||||
// 转发窗口尺寸变化给所有页签按钮和页面
|
||||
for (auto& c : controls)
|
||||
{
|
||||
c.first->onWindowResize();
|
||||
c.second->onWindowResize();
|
||||
}
|
||||
// 尺寸变化后需要重绘自身
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
int TabControl::getActiveIndex() const
|
||||
@@ -364,15 +387,9 @@ void TabControl::requestRepaint(Control* parent)
|
||||
for (auto& control : controls)
|
||||
{
|
||||
if (control.first->isDirty() && control.first->IsVisible())
|
||||
{
|
||||
control.first->draw();
|
||||
break;
|
||||
}
|
||||
else if (control.second->isDirty()&&control.second->IsVisible())
|
||||
{
|
||||
control.second->draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+112
-29
@@ -87,28 +87,35 @@ void Table::initTextWaH()
|
||||
if (h > maxLineH)
|
||||
maxLineH = h;
|
||||
|
||||
// 列的像素宽 = 内容宽 + 左右 padding
|
||||
// 表内容总宽 = Σ(列宽 + 列间距)
|
||||
int contentW = 0;
|
||||
for (size_t j = 0; j < colWidths.size(); ++j)
|
||||
contentW += (colWidths[j] + 2 * padX) + colGap;
|
||||
// 列宽包含左右 padding:在计算完最大文本宽度后,加上 2*padX 作为单元格内边距
|
||||
for (size_t j = 0; j < colWidths.size(); ++j) {
|
||||
colWidths[j] += 2 * padX;
|
||||
}
|
||||
|
||||
// 表头高 & 行高(与 drawHeader/drawTable 内部一致:+上下 padding)
|
||||
const int headerH = maxLineH + 2 * padY;
|
||||
const int rowH = maxLineH + 2 * padY;
|
||||
const int rowsH = rowH * rowsPerPage;
|
||||
// 表内容总宽 = Σ(列宽 + 列间距)
|
||||
int contentW = 0;
|
||||
for (size_t j = 0; j < colWidths.size(); ++j)
|
||||
contentW += colWidths[j] + colGap;
|
||||
|
||||
// 页脚:
|
||||
const int pageTextH = textheight(LPCTSTR(pageNumtext.c_str()));
|
||||
const int btnTextH = textheight(LPCTSTR("上一页"));
|
||||
const int btnPadV = TABLE_BTN_TEXT_PAD_V;
|
||||
const int btnH = btnTextH + 2 * btnPadV;
|
||||
const int footerPad = TABLE_FOOTER_PAD;
|
||||
const int footerH = (pageTextH > btnH ? pageTextH : btnH) + footerPad;
|
||||
// 表头高 & 行高(与 drawHeader/drawTable 内部一致:+上下 padding)
|
||||
const int headerH = maxLineH + 2 * padY;
|
||||
const int rowH = maxLineH + 2 * padY;
|
||||
const int rowsH = rowH * rowsPerPage;
|
||||
|
||||
// 最终表宽/高:内容 + 对称边框
|
||||
this->width = contentW + (border << 1);
|
||||
this->height = headerH + rowsH + footerH + (border << 1);
|
||||
// 页脚:
|
||||
const int pageTextH = textheight(LPCTSTR(pageNumtext.c_str()));
|
||||
const int btnTextH = textheight(LPCTSTR("上一页"));
|
||||
const int btnPadV = TABLE_BTN_TEXT_PAD_V;
|
||||
const int btnH = btnTextH + 2 * btnPadV;
|
||||
const int footerPad = TABLE_FOOTER_PAD;
|
||||
const int footerH = (pageTextH > btnH ? pageTextH : btnH) + footerPad;
|
||||
|
||||
// 最终表宽/高:内容 + 对称边框
|
||||
this->width = contentW + (border << 1);
|
||||
this->height = headerH + rowsH + footerH + (border << 1);
|
||||
// 记录原始宽高用于锚点布局的参考;此处仅在初始化单元尺寸时重置
|
||||
this->localWidth = this->width;
|
||||
this->localHeight = this->height;
|
||||
}
|
||||
|
||||
void Table::initButton()
|
||||
@@ -171,6 +178,7 @@ void Table::initButton()
|
||||
if (pageNum) pageNum->setDirty(true);
|
||||
}
|
||||
});
|
||||
isNeedButtonAndPageNum = false;
|
||||
}
|
||||
|
||||
void Table::initPageNum()
|
||||
@@ -214,7 +222,7 @@ void Table::drawPageNum()
|
||||
pageNumtext += "页/共";
|
||||
pageNumtext += std::to_string(totalPages);
|
||||
pageNumtext += "页";
|
||||
if (nullptr == pageNum)
|
||||
if (nullptr == pageNum || isNeedButtonAndPageNum)
|
||||
initPageNum();
|
||||
pageNum->setText(pageNumtext);
|
||||
pageNum->textStyle = this->textStyle;
|
||||
@@ -226,7 +234,7 @@ void Table::drawPageNum()
|
||||
|
||||
void Table::drawButton()
|
||||
{
|
||||
if (nullptr == prevButton || nullptr == nextButton)
|
||||
if ((nullptr == prevButton || nullptr == nextButton)|| isNeedButtonAndPageNum)
|
||||
initButton();
|
||||
|
||||
this->prevButton->textStyle = this->textStyle;
|
||||
@@ -242,6 +250,43 @@ void Table::drawButton()
|
||||
|
||||
}
|
||||
|
||||
void Table::setWidth(int width)
|
||||
{
|
||||
// 调整列宽以匹配新的表格总宽度。不修改 localWidth,避免累计误差。
|
||||
// 当 width 与当前 width 不同时,根据差值平均分配到各列,余数依次累加/扣减。
|
||||
const int ncols = static_cast<int>(colWidths.size());
|
||||
if (ncols <= 0) {
|
||||
this->width = width;
|
||||
isNeedButtonAndPageNum = true;
|
||||
return;
|
||||
}
|
||||
int diff = width - this->width;
|
||||
// 基础增量:整除部分
|
||||
int baseChange = diff / ncols;
|
||||
int remainder = diff % ncols;
|
||||
for (int i = 0; i < ncols; ++i) {
|
||||
int change = baseChange;
|
||||
if (remainder > 0) {
|
||||
change += 1;
|
||||
remainder -= 1;
|
||||
} else if (remainder < 0) {
|
||||
change -= 1;
|
||||
remainder += 1;
|
||||
}
|
||||
int newWidth = colWidths[i] + change;
|
||||
// 限制最小宽度为 1,防止出现负值
|
||||
if (newWidth < 1) newWidth = 1;
|
||||
colWidths[i] = newWidth;
|
||||
}
|
||||
this->width = width;
|
||||
// 需要重新布局页脚元素
|
||||
isNeedButtonAndPageNum = true;
|
||||
}
|
||||
|
||||
void Table::setHeight(int height)
|
||||
{
|
||||
}
|
||||
|
||||
Table::Table(int x, int y)
|
||||
:Control(x, y, 0, 0)
|
||||
{
|
||||
@@ -317,13 +362,27 @@ void Table::draw()
|
||||
setfillstyle((int)tableFillMode);
|
||||
setbkmode(TRANSPARENT);
|
||||
}
|
||||
//确保在绘制任何表格内容之前捕获背景
|
||||
// 临时恢复样式,确保捕获正确的背景
|
||||
if ((!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height)||!saveBkImage)
|
||||
saveBackground(this->x, this->y, this->width, this->height);
|
||||
// 恢复背景(清除旧内容)
|
||||
restBackground();
|
||||
// 绘制表头
|
||||
// 在绘制前先恢复并更新背景快照:
|
||||
// 如果已有快照且尺寸发生变化,先恢复旧快照以清除上一次绘制,然后丢弃旧快照再重新抓取新的区域。
|
||||
if (hasSnap)
|
||||
{
|
||||
// 始终先恢复旧背景,清除上一帧内容
|
||||
restBackground();
|
||||
// 当尺寸变化或缓存图像无效时,需要重新截图
|
||||
if (!saveBkImage || 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();
|
||||
// 绘制表头
|
||||
|
||||
dX = x;
|
||||
dY = y;
|
||||
@@ -365,7 +424,7 @@ bool Table::handleEvent(const ExMessage& msg)
|
||||
void Table::setHeaders(std::initializer_list<std::string> headers)
|
||||
{
|
||||
this->headers.clear();
|
||||
for (auto lis : headers)
|
||||
for (auto& lis : headers)
|
||||
this->headers.push_back(lis);
|
||||
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
|
||||
isNeedDrawHeaders = true; // 标记需要重新绘制表头
|
||||
@@ -464,6 +523,17 @@ void Table::setTableBorderWidth(int width)
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
void Table::onWindowResize()
|
||||
{
|
||||
Control::onWindowResize(); // 先处理自己
|
||||
if (this->prevButton && this->nextButton && this->pageNum)
|
||||
{
|
||||
prevButton->onWindowResize();
|
||||
nextButton->onWindowResize();
|
||||
pageNum->onWindowResize();
|
||||
}
|
||||
}
|
||||
|
||||
int Table::getCurrentPage() const
|
||||
{
|
||||
return this->currentPage;
|
||||
@@ -519,4 +589,17 @@ int Table::getTableBorderWidth() const
|
||||
return this->tableBorderWidth;
|
||||
}
|
||||
|
||||
int Table::getTableWidth() const
|
||||
{
|
||||
int temp = 0;
|
||||
for (auto& w : colWidths)
|
||||
temp += w;
|
||||
return temp;
|
||||
}
|
||||
|
||||
int Table::getTableHeight() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+678
-214
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user