Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53dc237e46 | |||
| 46febdb973 | |||
| f05962954f | |||
| 58d4e8ab2f | |||
| 5420bfd644 | |||
| cc08187ced | |||
| c10e72b3fe | |||
| 6218ba54e3 |
29
.github/workflows/mirror-to-gitcode.yml
vendored
Normal file
29
.github/workflows/mirror-to-gitcode.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: Mirror to GitCode
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "**" ] # 任意分支推送触发
|
||||||
|
create:
|
||||||
|
tags: [ "*" ] # 新建 tag 触发
|
||||||
|
workflow_dispatch: # 支持手动触发
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mirror:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # 必须拿全历史+所有 tag
|
||||||
|
- name: Push --mirror to GitCode
|
||||||
|
env:
|
||||||
|
GITCODE_USER: ${{ secrets.GITCODE_USER }}
|
||||||
|
GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
git config --global user.name "mirror-bot"
|
||||||
|
git config --global user.email "mirror-bot@users.noreply.github.com"
|
||||||
|
# 如果你的命名空间不是个人用户,而是组织,请把 ${GITCODE_USER} 换成组织名
|
||||||
|
git remote add gitcode https://${GITCODE_USER}:${GITCODE_TOKEN}@gitcode.com/${GITCODE_USER}/StellarX.git
|
||||||
|
git push --prune --mirror gitcode
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
[中文文档](CHANGELOG.md)
|
[中文文档](CHANGELOG.md)
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
### ✨ 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
|
## [v2.2.1] - 2025-11-04
|
||||||
|
|
||||||
==This release is a hotfix for v2.2.0==
|
==This release is a hotfix for v2.2.0==
|
||||||
|
|||||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -7,6 +7,69 @@ StellarX 项目所有显著的变化都将被记录在这个文件中。
|
|||||||
|
|
||||||
[English document](CHANGELOG.en.md)
|
[English document](CHANGELOG.en.md)
|
||||||
|
|
||||||
|
## [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
|
||||||
|
|
||||||
|
### ✨ 新增
|
||||||
|
|
||||||
|
- 新增`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.1] - 2025 - 11 - 4
|
||||||
|
|
||||||
==此版本为v2.2.0的修复版本==
|
==此版本为v2.2.0的修复版本==
|
||||||
|
|||||||
39
README.en.md
39
README.en.md
@@ -7,8 +7,8 @@
|
|||||||

|

|
||||||
[](https://github.com/Ysm-04/StellarX)
|
[](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.
|
**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.**
|
||||||
- Fixed issues where border remnants and functional buttons could persist after closing a Dialog.
|
|
||||||
|
- **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).
|
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
|
## 📦 Project Structure & Design Philosophy
|
||||||
@@ -181,6 +187,11 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|||||||
| `MessageBoxResult` | Result | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
| `MessageBoxResult` | Result | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
||||||
| `TabPlacement` | Tab position | `Top`, `Bottom`, `Left`, `Right` |
|
| `TabPlacement` | Tab position | `Top`, `Bottom`, `Left`, `Right` |
|
||||||
|
|
||||||
|
| Enum | Description | Common values |
|
||||||
|
| ------------ | ---------------------- | -------------------------------------------- |
|
||||||
|
| `LayoutMode` | 窗口布局模式 | `Fixed`, `AnchorToEdges` |
|
||||||
|
| `Anchor` | 控件相对于父容器的锚点 | `NoAnchor` ,`Left` , `Right`, `Top`,`Bottom` |
|
||||||
|
|
||||||
### Structs
|
### Structs
|
||||||
|
|
||||||
| Struct | Description |
|
| Struct | Description |
|
||||||
|
|||||||
42
README.md
42
README.md
@@ -2,13 +2,18 @@
|
|||||||
|
|
||||||
[English document](README.en.md)
|
[English document](README.en.md)
|
||||||
|
|
||||||
|
> 本仓库为 **StellarX** 主仓:开发与 Issue/PR 均在 GitHub 进行。
|
||||||
|
> GitCode 仅为只读镜像:如需反馈请到 GitHub:https://github.com/Ysm-04/StellarX
|
||||||
|
|
||||||
|
[](https://gitcode.com/Ysm-04/StellarX)
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||

|

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

|

|
||||||

|

|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -25,24 +30,30 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🆕v2.2.1(v2.2.0修复版)
|
## ==公告==
|
||||||
|
|
||||||
- 解决了使用Canvas和TabControl容器时,出现频闪问题
|
## 🙏 鸣谢
|
||||||
|
|
||||||
- 修复了Dialog对话框关闭时概率出边边框残留和功能按钮残留问题
|
- 感谢用户 [@To-KongBai](https://github.com/To-KongBai) 提供稳定复现步骤与关键现象对比(容器嵌套孙控件坐标转换问题),帮助我们快速确认多容器嵌套时的控件坐标转换问题并修复。([Issues#6](https://github.com/Ysm-04/StellarX/issues/6))
|
||||||
|
- 在即将上线的官网中(ICP备案中)我们计划加入一个贡献者鸣谢墙,欢迎各位用户反馈BUG或者分享自己用星垣做的界面,我们将认真阅读并收录鸣谢
|
||||||
|
- 真诚的感谢每一位反馈BUG的用户,你们的反馈将使星垣更加稳定和健壮
|
||||||
|
|
||||||
详情参考[更新日志](CHANGELOG.md)
|
---
|
||||||
|
|
||||||
|
## 🆕V2.3.1——重要更新
|
||||||
|
|
||||||
|
### ✨ 新增
|
||||||
|
|
||||||
## V2.2.0 有何变化
|
新增一个登录界面Demo在主仓库**examples/**目录下
|
||||||
|
|
||||||
- **新增 TabControl 控件,实现多页面选项卡界面:** 通过 `TabControl` 可以轻松创建选项卡式布局,支持页签在上下左右排列、点击切换显示不同内容页面。适用于设置面板、多视图切换等场景。
|
### ⚙️ 变更
|
||||||
- **控件显隐与布局响应能力增强:** 现在所有控件都可以使用统一接口动态隐藏或显示(`setIsVisible`),容器控件隐藏时其内部子控件会自动随之隐藏/显示。与此同时,引入控件对窗口尺寸变化的响应机制(`onWindowResize`),窗口拉伸后界面各元素可协调更新,杜绝拉伸过程中出现残影或错位。
|
|
||||||
- **文本样式机制完善:** Label 控件改用统一的文本样式结构 `ControlText`,开发者可方便地设置字体、颜色、大小等属性来定制 Label 的外观(替代旧接口,更加灵活)。Button 的 Tooltip 提示也支持更丰富的定制和针对切换状态的不同提示文本。
|
|
||||||
- **其他改进:** 框架底层的对话框管理增加了防重复弹出相同提示的机制,修复了一些细节 Bug 并优化了刷新效率,进一步提升了稳定性。
|
|
||||||
|
|
||||||
详见 `CHANGELOG.md / CHANGELOG.en.md` 获取完整更新列表。
|
- **Dialog背景快照机制:**`Dialog`不在自己抓取和销毁快照,**删除**重载的抓取和恢复快照的方法,完全交由基类`Canvas`处理,`Dialog`的`draw`方法中不在处理快照
|
||||||
|
- **窗口变化重绘时控件和窗口重绘的时机调整:**主事件循环中窗口大小发生变化时先处理控件尺寸,并回贴和释放旧快照,然后再重绘新尺寸窗口,最后绘制控件
|
||||||
|
|
||||||
|
本次针对用户反馈和已知内容进行了一些修复……
|
||||||
|
|
||||||
|
详细变更请参阅[更新日志](CHANGELOG.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -184,6 +195,13 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|||||||
| `MessageBoxResult` | 结果 | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
| `MessageBoxResult` | 结果 | `OK`, `Cancel`, `Yes`, `No`, `Abort`, `Retry`, `Ignore` |
|
||||||
| `TabPlacement` | 页签位置 | `Top`,`Bottom`,`Left`,`Right` |
|
| `TabPlacement` | 页签位置 | `Top`,`Bottom`,`Left`,`Right` |
|
||||||
|
|
||||||
|
| 枚举 | 描述 | 常用值 |
|
||||||
|
| ------------ | ---------------------- | -------------------------------------------- |
|
||||||
|
| `LayoutMode` | 窗口布局模式 | `Fixed`, `AnchorToEdges` |
|
||||||
|
| Anchor | 控件相对于父容器的锚点 | `NoAnchor` ,`Left` , `Right`, `Top`,`Bottom` |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 结构体
|
### 结构体
|
||||||
|
|
||||||
| 结构体 | 描述 |
|
| 结构体 | 描述 |
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
auto blackColor = RGB(202, 255, 255);
|
auto blackColor = RGB(202, 255, 255);
|
||||||
char initData[33] = "00000000000000000000000000000000";//初始数据
|
char initData[33] = "00000000000000000000000000000000";//初始数据
|
||||||
bool gSigned = false; //是否为有符号数
|
bool gSigned = false; //是否为有符号数
|
||||||
void main()
|
int main()
|
||||||
{
|
{
|
||||||
Window mainWindow(700,500,NULL,RGB(255,255,255), "寄存器查看工具 V1.0——我在人间做废物 (同类工具定制:3150131407(Q / V))");
|
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);
|
selectionAreaLabel->setTextdisap(true);
|
||||||
std::vector<std::unique_ptr<Label>>selectionAreaButtonLabel;
|
std::vector<std::unique_ptr<Label>>selectionAreaButtonLabel;
|
||||||
std::vector<std::unique_ptr<Button>>selectionAreaButton;
|
std::vector<std::unique_ptr<Button>>selectionAreaButton;
|
||||||
@@ -21,22 +21,22 @@ void main()
|
|||||||
|
|
||||||
selectionArea->setCanvasBkColor(blackColor);
|
selectionArea->setCanvasBkColor(blackColor);
|
||||||
selectionArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
selectionArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
|
|
||||||
for (int y = 0; y < 2; y ++)
|
for (int y = 0; y < 2; y++)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
for (int x = 0; x <16; x++)
|
for (int x = 0; x < 16; x++)
|
||||||
{
|
{
|
||||||
if (0 == y)
|
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;
|
os << std::setw(2) << std::setfill('0') << 31 - x;
|
||||||
selectionAreaButtonLabel.back()->setText(os.str());
|
selectionAreaButtonLabel.back()->setText(os.str());
|
||||||
selectionAreaButtonLabel.back()->setTextdisap(true);
|
selectionAreaButtonLabel.back()->setTextdisap(true);
|
||||||
|
|
||||||
selectionAreaButton.push_back(
|
selectionAreaButton.push_back(
|
||||||
std::make_unique<Button>(x * 35 + 42 + 28 * (x / 4), 58,20,32,"0",
|
std::make_unique<Button>(x * 35 + 27 + 28 * (x / 4), 58, 25, 30, "0",
|
||||||
blackColor,RGB(171, 196, 220),StellarX::ButtonMode::TOGGLE));
|
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE));
|
||||||
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
||||||
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
||||||
@@ -55,19 +55,19 @@ void main()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
selectionAreaButtonLabel.push_back(std::make_unique<Label>(x * 35 + 40 + 28 * (x / 4), 90, "", RGB(208, 208, 208)));
|
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;
|
os << std::setw(2) << std::setfill('0') << 15 - x;
|
||||||
selectionAreaButtonLabel.back()->setText(os.str());
|
selectionAreaButtonLabel.back()->setText(os.str());
|
||||||
selectionAreaButtonLabel.back()->setTextdisap(true);
|
selectionAreaButtonLabel.back()->setTextdisap(true);
|
||||||
|
|
||||||
selectionAreaButton.push_back(
|
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));
|
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE));
|
||||||
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
selectionAreaButton.back()->textStyle.color = RGB(226, 116, 152);
|
||||||
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
selectionAreaButton.back()->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
selectionAreaButton_ptr.push_back(selectionAreaButton.back().get());
|
||||||
int k =15 - x;
|
int k = 15 - x;
|
||||||
selectionAreaButton.back()->setOnToggleOnListener([k,btn = selectionAreaButton_ptr.back()]()
|
selectionAreaButton.back()->setOnToggleOnListener([k, btn = selectionAreaButton_ptr.back()]()
|
||||||
{
|
{
|
||||||
btn->setButtonText("1");
|
btn->setButtonText("1");
|
||||||
initData[k] = '1';
|
initData[k] = '1';
|
||||||
@@ -78,8 +78,8 @@ void main()
|
|||||||
initData[k] = '0';
|
initData[k] = '0';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
os.str("");
|
os.str("");
|
||||||
os.clear();
|
os.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,25 +90,34 @@ void main()
|
|||||||
selectionArea->addControl(std::move(s));
|
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);
|
function->setCanvasfillMode(StellarX::FillMode::Null);
|
||||||
|
function->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
auto bitInvert = std::make_unique<Canvas>(10,170,220,70);
|
function->setCanvasBkColor(blackColor);
|
||||||
auto leftShift = std::make_unique<Canvas>(240, 170, 220, 70);
|
auto bitInvert_que = std::make_unique<Canvas>(0, 0, 220, 70);
|
||||||
auto rightShift = std::make_unique<Canvas>(470, 170, 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->setCanvasBkColor(blackColor);
|
||||||
bitInvert->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
bitInvert->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
leftShift->setCanvasBkColor(blackColor);
|
leftShift->setCanvasBkColor(blackColor);
|
||||||
leftShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
leftShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
rightShift->setCanvasBkColor(blackColor);
|
rightShift->setCanvasBkColor(blackColor);
|
||||||
rightShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
rightShift->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
|
|
||||||
|
function->addControl(std::move(bitInvert_que));
|
||||||
auto bitInvertLabel = std::make_unique<Label>(18,160,"位取反");
|
function->addControl(std::move(leftShift_que));
|
||||||
|
function->addControl(std::move(rightShift_que));
|
||||||
|
|
||||||
|
auto bitInvertLabel = std::make_unique<Label>(13, -10, "位取反");
|
||||||
bitInvertLabel->setTextdisap(true);
|
bitInvertLabel->setTextdisap(true);
|
||||||
auto leftShiftLabel = std::make_unique<Label>(248, 160, "左移位");
|
auto leftShiftLabel = std::make_unique<Label>(13, -10, "左移位");
|
||||||
leftShiftLabel->setTextdisap(true);
|
leftShiftLabel->setTextdisap(true);
|
||||||
auto rightShiftLabel = std::make_unique<Label>(478, 160, "右移位");
|
auto rightShiftLabel = std::make_unique<Label>(13, -10, "右移位");
|
||||||
rightShiftLabel->setTextdisap(true);
|
rightShiftLabel->setTextdisap(true);
|
||||||
|
|
||||||
// ====== 公用小工具======
|
// ====== 公用小工具======
|
||||||
@@ -139,22 +148,22 @@ void main()
|
|||||||
|
|
||||||
//取反区控件
|
//取反区控件
|
||||||
std::array<std::unique_ptr<Label>, 4> bitInvertFunctionLabel;
|
std::array<std::unique_ptr<Label>, 4> bitInvertFunctionLabel;
|
||||||
bitInvertFunctionLabel[0] = std::make_unique<Label>(35, 180, "低位");
|
bitInvertFunctionLabel[0] = std::make_unique<Label>(30, 10, "低位");
|
||||||
bitInvertFunctionLabel[1] = std::make_unique<Label>(90, 180, "高位");
|
bitInvertFunctionLabel[1] = std::make_unique<Label>(90, 10, "高位");
|
||||||
bitInvertFunctionLabel[2] = std::make_unique<Label>(15, 198, "从");
|
bitInvertFunctionLabel[2] = std::make_unique<Label>(15, 38, "从");
|
||||||
bitInvertFunctionLabel[3] = std::make_unique<Label>(75, 198, "到");
|
bitInvertFunctionLabel[3] = std::make_unique<Label>(75, 38, "到");
|
||||||
|
|
||||||
std::array<std::unique_ptr<TextBox>, 2> bitInvertFunctionTextBox;
|
std::array<std::unique_ptr<TextBox>, 2> bitInvertFunctionTextBox;
|
||||||
bitInvertFunctionTextBox[0] = std::make_unique<TextBox>(35, 203, 35, 30, "0");
|
bitInvertFunctionTextBox[0] = std::make_unique<TextBox>(35, 35, 35, 30, "0");
|
||||||
bitInvertFunctionTextBox[1] = std::make_unique<TextBox>(95, 203, 35, 30, "0");
|
bitInvertFunctionTextBox[1] = std::make_unique<TextBox>(95, 35, 35, 30, "0");
|
||||||
auto invL = bitInvertFunctionTextBox[0].get();
|
auto invL = bitInvertFunctionTextBox[0].get();
|
||||||
auto invH = bitInvertFunctionTextBox[1].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));
|
blackColor, RGB(171, 196, 220));
|
||||||
bitInvertFunctionButton->textStyle.color = RGB(226, 116, 152);
|
bitInvertFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||||
bitInvertFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
bitInvertFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto bitInvertFunctionButton_ptr = bitInvertFunctionButton.get();
|
auto bitInvertFunctionButton_ptr = bitInvertFunctionButton.get();
|
||||||
|
|
||||||
bitInvert->addControl(std::move(bitInvertFunctionButton));
|
bitInvert->addControl(std::move(bitInvertFunctionButton));
|
||||||
bitInvert->addControl(std::move(bitInvertLabel));
|
bitInvert->addControl(std::move(bitInvertLabel));
|
||||||
for (auto& b : bitInvertFunctionTextBox)
|
for (auto& b : bitInvertFunctionTextBox)
|
||||||
@@ -171,22 +180,22 @@ void main()
|
|||||||
bitInvert->addControl(std::move(b));
|
bitInvert->addControl(std::move(b));
|
||||||
}
|
}
|
||||||
//左移控件
|
//左移控件
|
||||||
auto leftShiftFunctionLabel = std::make_unique<Label>(435, 198, "位");
|
auto leftShiftFunctionLabel = std::make_unique<Label>(198, 30, "位");
|
||||||
leftShiftFunctionLabel->setTextdisap(true);
|
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->setMaxCharLen(3);
|
||||||
leftShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
leftShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
||||||
leftShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
leftShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
||||||
leftShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
leftShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto shlBox = leftShiftFunctionTextBox.get();
|
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));
|
blackColor, RGB(171, 196, 220));
|
||||||
leftShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
leftShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||||
leftShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
leftShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto leftShiftFunctionButton_ptr = leftShiftFunctionButton.get();
|
auto leftShiftFunctionButton_ptr = leftShiftFunctionButton.get();
|
||||||
|
|
||||||
|
|
||||||
leftShift->addControl(std::move(leftShiftFunctionButton));
|
leftShift->addControl(std::move(leftShiftFunctionButton));
|
||||||
leftShift->addControl(std::move(leftShiftFunctionTextBox));
|
leftShift->addControl(std::move(leftShiftFunctionTextBox));
|
||||||
|
|
||||||
@@ -194,47 +203,44 @@ void main()
|
|||||||
leftShift->addControl(std::move(leftShiftFunctionLabel));
|
leftShift->addControl(std::move(leftShiftFunctionLabel));
|
||||||
|
|
||||||
//右移控件
|
//右移控件
|
||||||
auto rightShiftFunctionLabel = std::make_unique<Label>(665, 198, "位");
|
auto rightShiftFunctionLabel = std::make_unique<Label>(198, 30, "位");
|
||||||
rightShiftFunctionLabel->setTextdisap(true);
|
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->setMaxCharLen(3);
|
||||||
rightShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
rightShiftFunctionTextBox->textStyle.color = RGB(226, 116, 152);
|
||||||
rightShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
rightShiftFunctionTextBox->setTextBoxBk(RGB(244, 234, 142));
|
||||||
rightShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
rightShiftFunctionTextBox->setTextBoxshape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto shrBox = rightShiftFunctionTextBox.get();
|
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));
|
blackColor, RGB(171, 196, 220));
|
||||||
rightShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
rightShiftFunctionButton->textStyle.color = RGB(226, 116, 152);
|
||||||
rightShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
rightShiftFunctionButton->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto rightShiftFunctionButton_ptr = rightShiftFunctionButton.get();
|
auto rightShiftFunctionButton_ptr = rightShiftFunctionButton.get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rightShift->addControl(std::move(rightShiftFunctionButton));
|
rightShift->addControl(std::move(rightShiftFunctionButton));
|
||||||
|
|
||||||
rightShift->addControl(std::move(rightShiftFunctionTextBox));
|
rightShift->addControl(std::move(rightShiftFunctionTextBox));
|
||||||
rightShift->addControl(std::move(rightShiftLabel));
|
rightShift->addControl(std::move(rightShiftLabel));
|
||||||
rightShift->addControl(std::move(rightShiftFunctionLabel));
|
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);
|
auto NumericalDisplayArea = std::make_unique<Canvas>(10, 255, 680, 70);
|
||||||
NumericalDisplayArea->setCanvasBkColor(blackColor);
|
NumericalDisplayArea->setCanvasBkColor(blackColor);
|
||||||
NumericalDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
NumericalDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
|
|
||||||
std::array<std::unique_ptr<Label>, 3> NumericalDisplayAreaLabel;
|
std::array<std::unique_ptr<Label>, 3> NumericalDisplayAreaLabel;
|
||||||
NumericalDisplayAreaLabel[0] = std::make_unique<Label>(18, 245, "数值显示区");
|
NumericalDisplayAreaLabel[0] = std::make_unique<Label>(18, -10, "数值显示区");
|
||||||
NumericalDisplayAreaLabel[1] = std::make_unique<Label>(20, 278, "十六进制");
|
NumericalDisplayAreaLabel[1] = std::make_unique<Label>(20, 25, "十六进制");
|
||||||
NumericalDisplayAreaLabel[2] = std::make_unique<Label>(330, 278, "十进制");
|
NumericalDisplayAreaLabel[2] = std::make_unique<Label>(330, 25, "十进制");
|
||||||
|
|
||||||
std::array<std::unique_ptr<TextBox>, 2> NumericalDisplayAreaTextBox;
|
std::array<std::unique_ptr<TextBox>, 2> NumericalDisplayAreaTextBox;
|
||||||
NumericalDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 275, 200, 30, "0");
|
NumericalDisplayAreaTextBox[0] = std::make_unique<TextBox>(110, 25, 200, 30, "0");
|
||||||
NumericalDisplayAreaTextBox[1] = std::make_unique<TextBox>(400, 275, 200, 30, "0");
|
NumericalDisplayAreaTextBox[1] = std::make_unique<TextBox>(400, 25, 200, 30, "0");
|
||||||
auto hex = NumericalDisplayAreaTextBox[0].get();
|
auto hex = NumericalDisplayAreaTextBox[0].get();
|
||||||
auto dec = NumericalDisplayAreaTextBox[1].get();
|
auto dec = NumericalDisplayAreaTextBox[1].get();
|
||||||
|
|
||||||
@@ -257,15 +263,15 @@ void main()
|
|||||||
auto BinaryDisplayArea = std::make_unique<Canvas>(10, 335, 680, 110);
|
auto BinaryDisplayArea = std::make_unique<Canvas>(10, 335, 680, 110);
|
||||||
BinaryDisplayArea->setCanvasBkColor(blackColor);
|
BinaryDisplayArea->setCanvasBkColor(blackColor);
|
||||||
BinaryDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
BinaryDisplayArea->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
||||||
|
|
||||||
std::array<std::unique_ptr<Label>, 3> BinaryDisplayAreaLabel;
|
std::array<std::unique_ptr<Label>, 3> BinaryDisplayAreaLabel;
|
||||||
BinaryDisplayAreaLabel[0] = std::make_unique<Label>(18, 325, "二进制显示区");
|
BinaryDisplayAreaLabel[0] = std::make_unique<Label>(18, -10, "二进制显示区");
|
||||||
BinaryDisplayAreaLabel[1] = std::make_unique<Label>(35, 353, "上次值");
|
BinaryDisplayAreaLabel[1] = std::make_unique<Label>(35, 20, "上次值");
|
||||||
BinaryDisplayAreaLabel[2] = std::make_unique<Label>(35, 400, "本次值");
|
BinaryDisplayAreaLabel[2] = std::make_unique<Label>(35, 67, "本次值");
|
||||||
|
|
||||||
std::array<std::unique_ptr<TextBox>, 2> BinaryDisplayAreaTextBox;
|
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[0] = std::make_unique<TextBox>(110, 20, 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[1] = std::make_unique<TextBox>(110, 67, 520, 30, "0000_0000_0000_0000_0000_0000_0000_0000");
|
||||||
auto Last = BinaryDisplayAreaTextBox[0].get();
|
auto Last = BinaryDisplayAreaTextBox[0].get();
|
||||||
auto This = BinaryDisplayAreaTextBox[1].get();
|
auto This = BinaryDisplayAreaTextBox[1].get();
|
||||||
|
|
||||||
@@ -366,17 +372,17 @@ void main()
|
|||||||
auto configuration = std::make_unique<Canvas>(10, 455, 680, 40);
|
auto configuration = std::make_unique<Canvas>(10, 455, 680, 40);
|
||||||
configuration->setCanvasBkColor(blackColor);
|
configuration->setCanvasBkColor(blackColor);
|
||||||
configuration->setShape(StellarX::ControlShape::B_ROUND_RECTANGLE);
|
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);
|
configurationLabel->setTextdisap(true);
|
||||||
|
|
||||||
std::array<std::unique_ptr<Button>,2> configurationButton;
|
std::array<std::unique_ptr<Button>, 2> configurationButton;
|
||||||
configurationButton[0] = std::make_unique<Button>(450, 465, 80, 20, "一键置0",
|
configurationButton[0] = std::make_unique<Button>(420, 10, 90, 20, "一键置0",
|
||||||
blackColor, RGB(171, 196, 220));
|
blackColor, RGB(171, 196, 220));
|
||||||
configurationButton[0]->textStyle.color = RGB(226, 116, 152);
|
configurationButton[0]->textStyle.color = RGB(226, 116, 152);
|
||||||
configurationButton[0]->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
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));
|
blackColor, RGB(171, 196, 220));
|
||||||
configurationButton[1]->textStyle.color = RGB(226, 116, 152);
|
configurationButton[1]->textStyle.color = RGB(226, 116, 152);
|
||||||
configurationButton[1]->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
configurationButton[1]->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
@@ -419,16 +425,16 @@ void main()
|
|||||||
|
|
||||||
|
|
||||||
auto signedToggle = std::make_unique<Button>(
|
auto signedToggle = std::make_unique<Button>(
|
||||||
350, 465, 80, 20, "无符号",
|
330, 10, 80, 20, "无符号",
|
||||||
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE);
|
blackColor, RGB(171, 196, 220), StellarX::ButtonMode::TOGGLE);
|
||||||
signedToggle->textStyle.color = RGB(226, 116, 152);
|
signedToggle->textStyle.color = RGB(226, 116, 152);
|
||||||
signedToggle->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
signedToggle->setButtonShape(StellarX::ControlShape::B_RECTANGLE);
|
||||||
auto* signedTogglePtr = signedToggle.get();
|
auto* signedTogglePtr = signedToggle.get();
|
||||||
|
|
||||||
signedTogglePtr->setOnToggleOnListener([&]() {
|
signedTogglePtr->setOnToggleOnListener([&]() {
|
||||||
gSigned = true;
|
gSigned = true;
|
||||||
signedTogglePtr->setButtonText("有符号");
|
signedTogglePtr->setButtonText("有符号");
|
||||||
|
StellarX::MessageBox::showModal(mainWindow, "有符号模式下,\n最高位为符号位,\n其余位为数值位。", "有符号模式");
|
||||||
// 立即刷新十进制显示:用当前位图算出新值,仅改 dec
|
// 立即刷新十进制显示:用当前位图算出新值,仅改 dec
|
||||||
auto cur = snapshotBits();
|
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; }();
|
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([&]() {
|
signedTogglePtr->setOnToggleOffListener([&]() {
|
||||||
gSigned = false;
|
gSigned = false;
|
||||||
signedTogglePtr->setButtonText("无符号");
|
signedTogglePtr->setButtonText("无符号");
|
||||||
|
StellarX::MessageBox::showAsync(mainWindow, "无符号模式下,\n所有位均为数值位。", "无符号模式");
|
||||||
auto cur = snapshotBits();
|
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; }();
|
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));
|
dec->setText(std::to_string(u));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
signedTogglePtr->enableTooltip(true);
|
||||||
|
signedTogglePtr->setTooltipTextsForToggle("切换无符号模式", "切换有符号模式");
|
||||||
|
|
||||||
configuration->addControl(std::move(configurationButton[0]));
|
configuration->addControl(std::move(configurationButton[0]));
|
||||||
configuration->addControl(std::move(configurationButton[1]));
|
configuration->addControl(std::move(configurationButton[1]));
|
||||||
configuration->addControl(std::move(signedToggle));
|
configuration->addControl(std::move(signedToggle));
|
||||||
configuration->addControl(std::move(configurationLabel));
|
configuration->addControl(std::move(configurationLabel));
|
||||||
|
|
||||||
|
|
||||||
mainWindow.addControl(std::move(selectionArea));
|
mainWindow.addControl(std::move(selectionArea));
|
||||||
mainWindow.addControl(std::move(function));
|
mainWindow.addControl(std::move(function));
|
||||||
mainWindow.addControl(std::move(NumericalDisplayArea));
|
mainWindow.addControl(std::move(NumericalDisplayArea));
|
||||||
@@ -459,4 +466,4 @@ void main()
|
|||||||
|
|
||||||
mainWindow.draw();
|
mainWindow.draw();
|
||||||
return mainWindow.runEventLoop();
|
return mainWindow.runEventLoop();
|
||||||
}
|
}
|
||||||
127
examples/登录界面/LoginInterface.cpp
Normal file
127
examples/登录界面/LoginInterface.cpp
Normal 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();
|
||||||
|
}
|
||||||
BIN
examples/登录界面/image/bk1.jpg
Normal file
BIN
examples/登录界面/image/bk1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 MiB |
BIN
examples/登录界面/image/lo_bk2.jpg
Normal file
BIN
examples/登录界面/image/lo_bk2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
BIN
examples/登录界面/image/星垣logo.png
Normal file
BIN
examples/登录界面/image/星垣logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
image/1.png
Normal file
BIN
image/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
BIN
image/2.png
Normal file
BIN
image/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
@@ -25,6 +25,9 @@
|
|||||||
#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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Control.h"
|
#include "Control.h"
|
||||||
|
#include"Table.h"
|
||||||
|
|
||||||
class Canvas : public Control
|
class Canvas : public Control
|
||||||
{
|
{
|
||||||
@@ -39,6 +40,10 @@ public:
|
|||||||
Canvas();
|
Canvas();
|
||||||
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;
|
||||||
@@ -66,5 +71,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
//用来检查对话框是否模态,此控件不做实现
|
//用来检查对话框是否模态,此控件不做实现
|
||||||
bool model() const override { return false; };
|
bool model() const override { return false; };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,23 +9,20 @@
|
|||||||
* - 定义控件基本属性(坐标、尺寸、脏标记)
|
* - 定义控件基本属性(坐标、尺寸、脏标记)
|
||||||
* - 提供绘图状态管理(saveStyle/restoreStyle)
|
* - 提供绘图状态管理(saveStyle/restoreStyle)
|
||||||
* - 声明纯虚接口(draw、handleEvent等)
|
* - 声明纯虚接口(draw、handleEvent等)
|
||||||
* - 支持移动语义,禁止拷贝语义
|
* - 禁止移动语义,禁止拷贝语义
|
||||||
*
|
*
|
||||||
* @使用场景: 作为所有具体控件类的基类,不直接实例化
|
* @使用场景: 作为所有具体控件类的基类,不直接实例化
|
||||||
* @所属框架: 星垣(StellarX) GUI框架
|
* @所属框架: 星垣(StellarX) GUI框架
|
||||||
* @作者: 我在人间做废物
|
* @作者: 我在人间做废物
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef _WIN32_WINNT
|
#ifndef _WIN32_WINNT
|
||||||
#define _WIN32_WINNT 0x0600
|
#define _WIN32_WINNT 0x0600
|
||||||
#endif
|
#endif
|
||||||
#ifndef WINVER
|
#ifndef WINVER
|
||||||
#define WINVER _WIN32_WINNT
|
#define WINVER _WIN32_WINNT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <easyx.h>
|
#include <easyx.h>
|
||||||
@@ -45,6 +42,11 @@ protected:
|
|||||||
bool dirty = true; // 是否重绘
|
bool dirty = true; // 是否重绘
|
||||||
bool show = true; // 是否显示
|
bool show = true; // 是否显示
|
||||||
|
|
||||||
|
/* == 布局模式 == */
|
||||||
|
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
||||||
|
StellarX::Anchor anchor_1 = StellarX::Anchor::Top; // 锚点
|
||||||
|
StellarX::Anchor anchor_2 = StellarX::Anchor::Right; // 锚点
|
||||||
|
|
||||||
/* == 背景快照 == */
|
/* == 背景快照 == */
|
||||||
IMAGE* saveBkImage = nullptr;
|
IMAGE* saveBkImage = nullptr;
|
||||||
int saveBkX = 0, saveBkY = 0; // 快照保存起始坐标
|
int saveBkX = 0, saveBkY = 0; // 快照保存起始坐标
|
||||||
@@ -116,10 +118,10 @@ 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; }
|
||||||
void setWidth(int width) { this->width = width; dirty = true; }
|
virtual void setWidth(int width) { this->width = width; dirty = true; }
|
||||||
void setHeight(int height) { this->height = height; dirty = true; }
|
virtual void setHeight(int height) { this->height = height; dirty = true; }
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void draw() = 0;
|
virtual void draw() = 0;
|
||||||
@@ -130,7 +132,6 @@ public:
|
|||||||
void setParent(Control* parent) { this->parent = parent; }
|
void setParent(Control* parent) { this->parent = parent; }
|
||||||
//设置是否重绘
|
//设置是否重绘
|
||||||
virtual void setDirty(bool dirty) { this->dirty = dirty; }
|
virtual void setDirty(bool dirty) { this->dirty = dirty; }
|
||||||
|
|
||||||
//检查控件是否可见
|
//检查控件是否可见
|
||||||
bool IsVisible() const { return show; };
|
bool IsVisible() const { return show; };
|
||||||
//获取控件id
|
//获取控件id
|
||||||
@@ -139,6 +140,12 @@ public:
|
|||||||
bool isDirty() { return dirty; }
|
bool isDirty() { return dirty; }
|
||||||
//用来检查对话框是否模态,其他控件不用实现
|
//用来检查对话框是否模态,其他控件不用实现
|
||||||
virtual bool model()const = 0;
|
virtual bool model()const = 0;
|
||||||
|
//布局
|
||||||
|
void setLayoutMode(StellarX::LayoutMode layoutMode_);
|
||||||
|
void setAnchor(StellarX::Anchor anchor_1, StellarX::Anchor anchor_2);
|
||||||
|
StellarX::Anchor getAnchor_1() const;
|
||||||
|
StellarX::Anchor getAnchor_2() const;
|
||||||
|
StellarX::LayoutMode getLayoutMode() const;
|
||||||
protected:
|
protected:
|
||||||
void saveStyle();
|
void saveStyle();
|
||||||
void restoreStyle();
|
void restoreStyle();
|
||||||
|
|||||||
@@ -343,4 +343,47 @@ namespace StellarX
|
|||||||
Left,
|
Left,
|
||||||
Right
|
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -123,10 +123,7 @@ private:
|
|||||||
void getTextSize();
|
void getTextSize();
|
||||||
//初始化对话框尺寸
|
//初始化对话框尺寸
|
||||||
void initDialogSize();
|
void initDialogSize();
|
||||||
void saveBackground(int x, int y, int w, int h)override;
|
void addControl(std::unique_ptr<Control> control);
|
||||||
|
|
||||||
void restBackground()override;
|
|
||||||
|
|
||||||
|
|
||||||
// 清除所有控件
|
// 清除所有控件
|
||||||
void clearControls();
|
void clearControls();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* @文件: StellarX.h
|
* @文件: StellarX.h
|
||||||
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
|
* @摘要: 星垣(StellarX) GUI框架 - 主包含头文件
|
||||||
* @版本: v2.2.1
|
* @版本: v2.3.1
|
||||||
* @描述:
|
* @描述:
|
||||||
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
|
* 一个为Windows平台打造的轻量级、模块化C++ GUI框架。
|
||||||
* 基于EasyX图形库,提供简洁易用的API和丰富的控件。
|
* 基于EasyX图形库,提供简洁易用的API和丰富的控件。
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
* @作者: 我在人间做废物
|
* @作者: 我在人间做废物
|
||||||
* @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com]
|
* @邮箱: [3150131407@qq.com] | [ysm3150131407@gmail.com]
|
||||||
* @仓库: [https://github.com/Ysm-04/StellarX]
|
* @仓库: [https://github.com/Ysm-04/StellarX]
|
||||||
|
* @官网:即将上线,敬请期待
|
||||||
*
|
*
|
||||||
* @许可证: MIT License
|
* @许可证: MIT License
|
||||||
* @版权: Copyright (c) 2025 我在人间做废物
|
* @版权: Copyright (c) 2025 我在人间做废物
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ 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;
|
||||||
@@ -64,5 +67,6 @@ 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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ private:
|
|||||||
bool isShowPageButton = true; // 是否显示翻页按钮
|
bool isShowPageButton = true; // 是否显示翻页按钮
|
||||||
bool isNeedDrawHeaders = true; // 是否需要绘制表头
|
bool isNeedDrawHeaders = true; // 是否需要绘制表头
|
||||||
bool isNeedCellSize = true; // 是否需要计算单元格尺寸
|
bool isNeedCellSize = true; // 是否需要计算单元格尺寸
|
||||||
|
bool isNeedButtonAndPageNum = true; // 是否需要计算翻页按钮和页码信息
|
||||||
|
|
||||||
Button* prevButton = nullptr; // 上一页按钮
|
Button* prevButton = nullptr; // 上一页按钮
|
||||||
Button* nextButton = nullptr; // 下一页按钮
|
Button* nextButton = nullptr; // 下一页按钮
|
||||||
@@ -98,7 +99,10 @@ 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 setHeight(int height) override;
|
||||||
public:
|
public:
|
||||||
Table(int x, int y);
|
Table(int x, int y);
|
||||||
~Table();
|
~Table();
|
||||||
@@ -127,6 +131,8 @@ public:
|
|||||||
void setTableLineStyle(StellarX::LineStyle style);
|
void setTableLineStyle(StellarX::LineStyle style);
|
||||||
//设置边框宽度
|
//设置边框宽度
|
||||||
void setTableBorderWidth(int width);
|
void setTableBorderWidth(int width);
|
||||||
|
//窗口变化丢快照+标脏
|
||||||
|
void onWindowResize() override;
|
||||||
|
|
||||||
//************************** 获取属性 *****************************/
|
//************************** 获取属性 *****************************/
|
||||||
|
|
||||||
@@ -152,6 +158,9 @@ public:
|
|||||||
std::vector<std::vector<std::string>> getData() const;
|
std::vector<std::vector<std::string>> getData() const;
|
||||||
//获取表格边框宽度
|
//获取表格边框宽度
|
||||||
int getTableBorderWidth() const;
|
int getTableBorderWidth() const;
|
||||||
|
//获取表格尺寸
|
||||||
|
int getTableWidth() const;
|
||||||
|
int getTableHeight() const;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
#pragma once
|
||||||
#include"Control.h"
|
|
||||||
/*******************************************************************************
|
#include "Control.h"
|
||||||
* @类: Window
|
#include <string>
|
||||||
* @摘要: 应用程序主窗口类,管理窗口生命周期和消息循环
|
#include <vector>
|
||||||
* @描述:
|
#include <memory>
|
||||||
* 创建和管理应用程序主窗口,作为所有控件的根容器。
|
#include <windows.h>
|
||||||
* 处理消息分发、事件循环和渲染调度。
|
|
||||||
*
|
|
||||||
* @特性:
|
|
||||||
* - 多种窗口模式配置(双缓冲、控制台等)
|
|
||||||
* - 背景图片和颜色支持
|
|
||||||
* - 集成的对话框管理系统
|
|
||||||
* - 完整的消息处理循环
|
|
||||||
* - 控件和对话框的生命周期管理
|
|
||||||
*
|
|
||||||
* @使用场景: 应用程序主窗口,GUI程序的入口和核心
|
|
||||||
* @所属框架: 星垣(StellarX) GUI框架
|
|
||||||
* @作者: 我在人间做废物
|
|
||||||
******************************************************************************/
|
|
||||||
class Window
|
class Window
|
||||||
{
|
{
|
||||||
int width; //窗口宽度
|
// —— 尺寸状态 ——(绘制尺寸与待应用尺寸分离;收口时一次性更新)
|
||||||
int height; //窗口高度
|
int width; // 当前有效宽(已应用到画布/控件的客户区宽)
|
||||||
int windowMode = NULL; //窗口模式
|
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 切换)
|
||||||
|
|
||||||
// --- 尺寸变化去抖用 ---
|
// —— 原生窗口句柄与子类化钩子 ——(子类化 EasyX 的窗口过程以拦截关键消息)
|
||||||
int pendingW;
|
HWND hWnd = NULL; // EasyX 初始化后的窗口句柄
|
||||||
int pendingH;
|
WNDPROC oldWndProc = nullptr; // 保存旧过程(CallWindowProc 回落)
|
||||||
bool needResizeDirty = false;
|
bool procHooked = false; // 避免重复子类化
|
||||||
|
static LRESULT CALLBACK WndProcThunk(HWND h, UINT m, WPARAM w, LPARAM l); // 静态过程分发到 this
|
||||||
|
|
||||||
HWND hWnd = NULL; //窗口句柄
|
// —— 绘制相关 ——(是否使用合成双缓冲、窗口标题、背景等)
|
||||||
std::string headline; //窗口标题
|
bool useComposited = true; // 是否启用 WS_EX_COMPOSITED(部分机器可能增加一帧观感延迟)
|
||||||
COLORREF wBkcolor = BLACK; //窗口背景
|
std::string headline; // 窗口标题文本
|
||||||
IMAGE* background = nullptr; //窗口背景图片
|
COLORREF wBkcolor = BLACK; // 纯色背景(无背景图时使用)
|
||||||
std::string bkImageFile; //窗口背景图片文件名
|
IMAGE* background = nullptr; // 背景图对象指针(存在时优先绘制)
|
||||||
std::vector<std::unique_ptr<Control>> controls; //控件管理
|
std::string bkImageFile; // 背景图文件路径(loadimage 用)
|
||||||
std::vector<std::unique_ptr<Control>> dialogs; //对话框管理
|
|
||||||
|
|
||||||
|
// —— 控件/对话框 ——(容器内的普通控件与非模态对话框)
|
||||||
|
std::vector<std::unique_ptr<Control>> controls;
|
||||||
|
std::vector<std::unique_ptr<Control>> dialogs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool dialogClose = false; //是否有对话框关闭
|
bool dialogClose = false; // 项目内使用的状态位
|
||||||
|
|
||||||
Window(int width, int height, int mode);
|
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
||||||
Window(int width, int height, int mode, COLORREF bkcloc);
|
Window(int width, int height, int mode);
|
||||||
Window(int width, int height, int mode , COLORREF bkcloc, std::string headline = "窗口");
|
Window(int width, int height, int mode, COLORREF bkcloc);
|
||||||
~Window();
|
Window(int width, int height, int mode, COLORREF bkcloc, std::string headline);
|
||||||
//绘制窗口
|
~Window();
|
||||||
void draw();
|
|
||||||
void draw(std::string pImgFile);
|
// —— 绘制与事件循环 ——(draw* 完成一次全量绘制;runEventLoop 驱动事件与统一收口)
|
||||||
//事件循环
|
void draw(); // 纯色背景版本
|
||||||
int runEventLoop();
|
void draw(std::string pImgFile); // 背景图版本
|
||||||
//设置窗口背景图片
|
int runEventLoop(); // 主事件循环(PeekMessage + 统一收口重绘)
|
||||||
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;
|
void setBkImage(std::string pImgFile);
|
||||||
//获取窗口宽度
|
void setBkcolor(COLORREF c);
|
||||||
int getWidth() const;
|
void setHeadline(std::string headline);
|
||||||
//获取窗口高度
|
|
||||||
int getHeight() const;
|
// —— 控件/对话框管理 ——(添加到容器,或做存在性判断)
|
||||||
//获取窗口标题
|
void addControl(std::unique_ptr<Control> control);
|
||||||
std::string getHeadline() const;
|
void addDialog(std::unique_ptr<Control> dialogs);
|
||||||
//获取窗口背景颜色
|
bool hasNonModalDialogWithCaption(const std::string& caption, const std::string& message) const;
|
||||||
COLORREF getBkcolor() const;
|
|
||||||
//获取窗口背景图片
|
// —— 访问器 ——(只读接口,供外部查询当前窗口/标题/背景等)
|
||||||
IMAGE* getBkImage() const;
|
HWND getHwnd() const;
|
||||||
//获取窗口背景图片文件名
|
int getWidth() const;
|
||||||
std::string getBkImageFile() const;
|
int getHeight() const;
|
||||||
//获取控件管理
|
std::string getHeadline() const;
|
||||||
std::vector<std::unique_ptr<Control>>& getControls();
|
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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
134
src/Button.cpp
134
src/Button.cpp
@@ -22,7 +22,7 @@ static inline int gbk_char_len(const std::string& s, size_t i)
|
|||||||
{
|
{
|
||||||
unsigned char b = (unsigned char)s[i];
|
unsigned char b = (unsigned char)s[i];
|
||||||
if (b <= 0x7F) return 1; // ASCII
|
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];
|
unsigned char b2 = (unsigned char)s[i + 1];
|
||||||
if (b2 >= 0x40 && b2 <= 0xFE && b2 != 0x7F) return 2; // 合法双字节
|
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; // 容错
|
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.empty() && s.back() == ' ') s.pop_back(); // ASCII 空格
|
||||||
while (s.size() >= 2)
|
while (s.size() >= 2)
|
||||||
{ // 全角空格 A1 A1
|
{ // 全角空格 A1 A1
|
||||||
unsigned char a = (unsigned char)s[s.size() - 2];
|
unsigned char a = (unsigned char)s[s.size() - 2];
|
||||||
unsigned char b = (unsigned char)s[s.size() - 1];
|
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;
|
for (unsigned char c : s) if (c > 0x7F) return false;
|
||||||
return true;
|
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 == ':';
|
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 (maxW <= 0) return "";
|
||||||
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
||||||
|
|
||||||
const std::string ell = "...";
|
const std::string ell = "...";
|
||||||
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
||||||
if (ellW > maxW)
|
if (ellW > maxW)
|
||||||
{ // 连 ... 都放不下
|
{ // 连 ... 都放不下
|
||||||
std::string e = ell;
|
std::string e = ell;
|
||||||
while (!e.empty() && textwidth(LPCTSTR(e.c_str())) > maxW) e.pop_back();
|
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;
|
size_t i = 0, lastFit = 0;
|
||||||
while (i < text.size())
|
while (i < text.size())
|
||||||
{
|
{
|
||||||
int clen = gbk_char_len(text, i);
|
int clen = gbk_char_len(text, i);
|
||||||
size_t j = text.size() < i + (size_t)clen ? text.size() : i + (size_t)clen;
|
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;
|
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];
|
unsigned char c = (unsigned char)text[k - 1];
|
||||||
if (c <= 0x7F && is_word_boundary_char(c)) { cutPos = k - 1; break; }
|
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字节)回退;省略号用全角 "…"
|
// 中文优先策略:严格逐“字符”(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 (maxW <= 0) return "";
|
||||||
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
if (textwidth(LPCTSTR(text.c_str())) <= maxW) return text;
|
||||||
|
|
||||||
std::string ell = ellipsis ? ellipsis : "…";
|
std::string ell = ellipsis ? ellipsis : "…";
|
||||||
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
int ellW = textwidth(LPCTSTR(ell.c_str()));
|
||||||
if (ellW > maxW)
|
if (ellW > maxW)
|
||||||
{ // 连省略号都放不下
|
{ // 连省略号都放不下
|
||||||
std::string e = ell;
|
std::string e = ell;
|
||||||
while (!e.empty() && textwidth(LPCTSTR(e.c_str())) > maxW) e.pop_back();
|
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;
|
const int limit = maxW - ellW;
|
||||||
|
|
||||||
size_t i = 0, lastFit = 0;
|
size_t i = 0, lastFit = 0;
|
||||||
while (i < text.size())
|
while (i < text.size())
|
||||||
{
|
{
|
||||||
int clen = gbk_char_len(text, i);
|
int clen = gbk_char_len(text, i);
|
||||||
size_t j = text.size() < i + (size_t)clen ? text.size() : i + (size_t)clen;
|
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()
|
Button::~Button()
|
||||||
{
|
{
|
||||||
if (buttonFileIMAGE)
|
if (buttonFileIMAGE)
|
||||||
delete buttonFileIMAGE;
|
delete buttonFileIMAGE;
|
||||||
buttonFileIMAGE = nullptr;
|
buttonFileIMAGE = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +224,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();
|
||||||
//根据按钮形状绘制
|
//根据按钮形状绘制
|
||||||
@@ -314,7 +314,7 @@ 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;
|
||||||
@@ -327,7 +327,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NORMAL 模式:鼠标在按钮上释放时才触发点击回调,如果移出区域则取消点击状态。
|
// NORMAL 模式:鼠标在按钮上释放时才触发点击回调,如果移出区域则取消点击状态。
|
||||||
// TOGGLE 模式:在释放时切换状态,并触发相应的开/关回调。
|
// TOGGLE 模式:在释放时切换状态,并触发相应的开/关回调。
|
||||||
else if (msg.message == WM_LBUTTONUP && hover && mode != StellarX::ButtonMode::DISABLED)
|
else if (msg.message == WM_LBUTTONUP && hover && mode != StellarX::ButtonMode::DISABLED)
|
||||||
{
|
{
|
||||||
hideTooltip(); // 隐藏悬停提示
|
hideTooltip(); // 隐藏悬停提示
|
||||||
@@ -349,7 +349,7 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
consume = true;
|
consume = true;
|
||||||
refreshTooltipTextForState();
|
refreshTooltipTextForState();
|
||||||
hideTooltip();
|
hideTooltip();
|
||||||
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
||||||
flushmessage(EX_MOUSE | EX_KEY);
|
flushmessage(EX_MOUSE | EX_KEY);
|
||||||
}
|
}
|
||||||
@@ -367,23 +367,23 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tipEnabled)
|
if (tipEnabled)
|
||||||
{
|
{
|
||||||
if (hover && !oldHover)
|
if (hover && !oldHover)
|
||||||
{
|
{
|
||||||
// 刚刚进入悬停:开计时,暂不显示
|
// 刚刚进入悬停:开计时,暂不显示
|
||||||
tipHoverTick = GetTickCount64();
|
tipHoverTick = GetTickCount64();
|
||||||
tipVisible = false;
|
tipVisible = false;
|
||||||
}
|
}
|
||||||
if (!hover && oldHover)
|
if (!hover && oldHover)
|
||||||
{
|
{
|
||||||
// 刚移出:立即隐藏
|
// 刚移出:立即隐藏
|
||||||
hideTooltip();
|
hideTooltip();
|
||||||
}
|
}
|
||||||
if (hover && !tipVisible)
|
if (hover && !tipVisible)
|
||||||
{
|
{
|
||||||
// 到点就显示
|
// 到点就显示
|
||||||
if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs)
|
if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs)
|
||||||
{
|
{
|
||||||
tipVisible = true;
|
tipVisible = true;
|
||||||
|
|
||||||
@@ -420,18 +420,18 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
|
|
||||||
if (tipEnabled && tipVisible)
|
if (tipEnabled && tipVisible)
|
||||||
tipLabel.draw();
|
tipLabel.draw();
|
||||||
|
|
||||||
return consume;
|
return consume;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setOnClickListener(const std::function<void()>&& callback)
|
void Button::setOnClickListener(const std::function<void()>&& callback)
|
||||||
{
|
{
|
||||||
this->onClickCallback = callback;
|
this->onClickCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setOnToggleOnListener(const std::function<void()>&& callback)
|
void Button::setOnToggleOnListener(const std::function<void()>&& callback)
|
||||||
{
|
{
|
||||||
this->onToggleOnCallback = callback;
|
this->onToggleOnCallback = callback;
|
||||||
}
|
}
|
||||||
void Button::setOnToggleOffListener(const std::function<void()>&& 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)
|
if (this->mode == StellarX::ButtonMode::DISABLED && mode != StellarX::ButtonMode::DISABLED)
|
||||||
textStyle.bStrikeOut = false;
|
textStyle.bStrikeOut = false;
|
||||||
//取值范围参考 buttMode的枚举注释
|
//取值范围参考 buttMode的枚举注释
|
||||||
this->mode = mode;
|
this->mode = mode;
|
||||||
dirty = true; // 标记需要重绘
|
dirty = true; // 标记需要重绘
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setROUND_RECTANGLEwidth(int width)
|
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)
|
||||||
{
|
{
|
||||||
rouRectangleSize.ROUND_RECTANGLEheight = height;
|
rouRectangleSize.ROUND_RECTANGLEheight = height;
|
||||||
this->dirty = true; // 标记需要重绘
|
this->dirty = true; // 标记需要重绘
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Button::isClicked() const
|
bool Button::isClicked() const
|
||||||
{
|
{
|
||||||
return this->click;
|
return this->click;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setFillMode(StellarX::FillMode mode)
|
void Button::setFillMode(StellarX::FillMode mode)
|
||||||
{
|
{
|
||||||
this->buttonFillMode = mode;
|
this->buttonFillMode = mode;
|
||||||
this->dirty = true; // 标记需要重绘
|
this->dirty = true; // 标记需要重绘
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setFillIma(StellarX::FillStyle ima)
|
void Button::setFillIma(StellarX::FillStyle ima)
|
||||||
{
|
{
|
||||||
buttonFillIma = ima;
|
buttonFillIma = ima;
|
||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,8 +484,8 @@ void Button::setFillIma(std::string imaNAme)
|
|||||||
delete buttonFileIMAGE;
|
delete buttonFileIMAGE;
|
||||||
buttonFileIMAGE = nullptr;
|
buttonFileIMAGE = nullptr;
|
||||||
}
|
}
|
||||||
buttonFileIMAGE = new IMAGE;
|
buttonFileIMAGE = new IMAGE;
|
||||||
loadimage(buttonFileIMAGE, imaNAme.c_str(),width,height);
|
loadimage(buttonFileIMAGE, imaNAme.c_str(), width, height);
|
||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +493,7 @@ void Button::setFillIma(std::string imaNAme)
|
|||||||
void Button::setButtonBorder(COLORREF Border)
|
void Button::setButtonBorder(COLORREF Border)
|
||||||
{
|
{
|
||||||
buttonBorderColor = Border;
|
buttonBorderColor = Border;
|
||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setButtonFalseColor(COLORREF color)
|
void Button::setButtonFalseColor(COLORREF color)
|
||||||
@@ -504,7 +504,7 @@ void Button::setButtonFalseColor(COLORREF color)
|
|||||||
|
|
||||||
void Button::setButtonText(const char* text)
|
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_width = textwidth(LPCTSTR(this->text.c_str()));
|
||||||
this->text_height = textheight(LPCTSTR(this->text.c_str()));
|
this->text_height = textheight(LPCTSTR(this->text.c_str()));
|
||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
@@ -526,7 +526,7 @@ void Button::setButtonText(std::string text)
|
|||||||
|
|
||||||
void Button::setButtonShape(StellarX::ControlShape shape)
|
void Button::setButtonShape(StellarX::ControlShape shape)
|
||||||
{
|
{
|
||||||
this->shape = shape;
|
this->shape = shape;
|
||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
this->needCutText = true;
|
this->needCutText = true;
|
||||||
}
|
}
|
||||||
@@ -535,7 +535,7 @@ void Button::setButtonShape(StellarX::ControlShape shape)
|
|||||||
void Button::setButtonClick(BOOL click)
|
void Button::setButtonClick(BOOL click)
|
||||||
{
|
{
|
||||||
this->click = click;
|
this->click = click;
|
||||||
|
|
||||||
if (mode == StellarX::ButtonMode::NORMAL && click)
|
if (mode == StellarX::ButtonMode::NORMAL && click)
|
||||||
{
|
{
|
||||||
if (onClickCallback) onClickCallback();
|
if (onClickCallback) onClickCallback();
|
||||||
@@ -550,7 +550,7 @@ void Button::setButtonClick(BOOL click)
|
|||||||
else if (!click && onToggleOffCallback) onToggleOffCallback();
|
else if (!click && onToggleOffCallback) onToggleOffCallback();
|
||||||
dirty = true;
|
dirty = true;
|
||||||
refreshTooltipTextForState();
|
refreshTooltipTextForState();
|
||||||
hideTooltip();
|
hideTooltip();
|
||||||
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
// 清除消息队列中积压的鼠标和键盘消息,防止本次点击事件被重复处理
|
||||||
flushmessage(EX_MOUSE | EX_KEY);
|
flushmessage(EX_MOUSE | EX_KEY);
|
||||||
}
|
}
|
||||||
@@ -561,52 +561,52 @@ void Button::setButtonClick(BOOL click)
|
|||||||
|
|
||||||
std::string Button::getButtonText() const
|
std::string Button::getButtonText() const
|
||||||
{
|
{
|
||||||
return this->text;
|
return this->text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Button::getButtonText_c() const
|
const char* Button::getButtonText_c() const
|
||||||
{
|
{
|
||||||
return this->text.c_str();
|
return this->text.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
StellarX::ButtonMode Button::getButtonMode() const
|
StellarX::ButtonMode Button::getButtonMode() const
|
||||||
{
|
{
|
||||||
return this->mode;
|
return this->mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
StellarX::ControlShape Button::getButtonShape() const
|
StellarX::ControlShape Button::getButtonShape() const
|
||||||
{
|
{
|
||||||
return this->shape;
|
return this->shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
StellarX::FillMode Button::getFillMode() const
|
StellarX::FillMode Button::getFillMode() const
|
||||||
{
|
{
|
||||||
return this->buttonFillMode;
|
return this->buttonFillMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
StellarX::FillStyle Button::getFillIma() const
|
StellarX::FillStyle Button::getFillIma() const
|
||||||
{
|
{
|
||||||
return this->buttonFillIma;
|
return this->buttonFillIma;
|
||||||
}
|
}
|
||||||
|
|
||||||
IMAGE* Button::getFillImaImage() const
|
IMAGE* Button::getFillImaImage() const
|
||||||
{
|
{
|
||||||
return this->buttonFileIMAGE;
|
return this->buttonFileIMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
COLORREF Button::getButtonBorder() const
|
COLORREF Button::getButtonBorder() const
|
||||||
{
|
{
|
||||||
return this->buttonBorderColor;
|
return this->buttonBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
COLORREF Button::getButtonTextColor() const
|
COLORREF Button::getButtonTextColor() const
|
||||||
{
|
{
|
||||||
return this->textStyle.color;
|
return this->textStyle.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
StellarX::ControlText Button::getButtonTextStyle() const
|
StellarX::ControlText Button::getButtonTextStyle() const
|
||||||
{
|
{
|
||||||
return this->textStyle;
|
return this->textStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Button::getButtonWidth() const
|
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)
|
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));
|
||||||
if (dis <= radius)
|
if (dis <= radius)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Button::isMouseInEllipse(int mouseX, int mouseY, int x, int y, int width, int height)
|
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 centerY = (y + height) / 2;
|
||||||
int majorAxis = (width - x) / 2;
|
int majorAxis = (width - x) / 2;
|
||||||
int minorAxis = (height - y) / 2;
|
int minorAxis = (height - y) / 2;
|
||||||
double dx = mouseX - centerX;
|
double dx = mouseX - centerX;
|
||||||
double dy = mouseY - centerY;
|
double dy = mouseY - centerY;
|
||||||
double normalizedDistance = (dx * dx) / (majorAxis * majorAxis) + (dy * dy) / (minorAxis * minorAxis);
|
double normalizedDistance = (dx * dx) / (majorAxis * majorAxis) + (dy * dy) / (minorAxis * minorAxis);
|
||||||
|
|
||||||
// 判断鼠标是否在椭圆内
|
// 判断鼠标是否在椭圆内
|
||||||
if (normalizedDistance <= 1.0)
|
if (normalizedDistance <= 1.0)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::cutButtonText()
|
void Button::cutButtonText()
|
||||||
@@ -659,18 +659,18 @@ void Button::cutButtonText()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 放不下:按语言偏好裁切(ASCII→词边界;CJK→逐字符,不撕裂双字节)
|
// 放不下:按语言偏好裁切(ASCII→词边界;CJK→逐字符,不撕裂双字节)
|
||||||
if (is_ascii_only(this->text))
|
if (is_ascii_only(this->text))
|
||||||
{
|
{
|
||||||
cutText = ellipsize_ascii_pref(this->text, contentW); // "..."
|
cutText = ellipsize_ascii_pref(this->text, contentW); // "..."
|
||||||
}
|
}
|
||||||
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()
|
||||||
@@ -686,9 +686,9 @@ void Button::hideTooltip()
|
|||||||
void Button::refreshTooltipTextForState()
|
void Button::refreshTooltipTextForState()
|
||||||
{
|
{
|
||||||
if (tipUserOverride) return; // 用户显式设置过 tipText,保持不变
|
if (tipUserOverride) return; // 用户显式设置过 tipText,保持不变
|
||||||
if(mode==StellarX::ButtonMode::NORMAL)
|
if (mode == StellarX::ButtonMode::NORMAL)
|
||||||
tipLabel.setText(tipTextClick);
|
tipLabel.setText(tipTextClick);
|
||||||
else if(mode==StellarX::ButtonMode::TOGGLE)
|
else if (mode == StellarX::ButtonMode::TOGGLE)
|
||||||
tipLabel.setText(click ? tipTextOn : tipTextOff);
|
tipLabel.setText(click ? tipTextOn : tipTextOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
240
src/Canvas.cpp
240
src/Canvas.cpp
@@ -12,26 +12,71 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Canvas::draw()
|
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();
|
saveStyle();
|
||||||
|
|
||||||
setlinecolor(canvasBorderClor);//设置线色
|
setlinecolor(canvasBorderClor);//设置线色
|
||||||
setfillcolor(canvasBkClor);//设置填充色
|
if(StellarX::FillMode::Null != canvasFillMode)
|
||||||
|
setfillcolor(canvasBkClor);//设置填充色
|
||||||
setfillstyle((int)canvasFillMode);//设置填充模式
|
setfillstyle((int)canvasFillMode);//设置填充模式
|
||||||
setlinestyle((int)canvasLineStyle, canvaslinewidth);
|
setlinestyle((int)canvasLineStyle, canvaslinewidth);
|
||||||
|
|
||||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
// 在绘制画布之前,先恢复并更新背景快照:
|
||||||
saveBackground(x, y, width, height);
|
// 1. 如果已有快照,则先回贴旧快照以清除之前的内容。
|
||||||
// 恢复背景(清除旧内容)
|
// 2. 当坐标或尺寸变化,或缓存图像无效时,丢弃旧快照并重新抓取新的背景。
|
||||||
restBackground();
|
int margin = canvaslinewidth > 1 ? canvaslinewidth : 1;
|
||||||
|
if (hasSnap)
|
||||||
|
{
|
||||||
|
// 恢复旧快照,清除上一次绘制
|
||||||
|
restBackground();
|
||||||
|
// 如果位置或尺寸变了,或没有有效缓存,则重新抓取
|
||||||
|
if (!saveBkImage || saveBkX != this->x - margin || saveBkY != this->y - margin || saveWidth != this->width + margin * 2 || saveHeight != this->height + margin * 2)
|
||||||
|
{
|
||||||
|
discardBackground();
|
||||||
|
saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 首次绘制或没有快照时直接抓取背景
|
||||||
|
saveBackground(this->x- margin, this->y- margin, this->width + margin*2, this->height + margin*2);
|
||||||
|
}
|
||||||
|
// 再次恢复最新快照,确保绘制区域干净
|
||||||
|
restBackground();
|
||||||
//根据画布形状绘制
|
//根据画布形状绘制
|
||||||
switch (shape)
|
switch (shape)
|
||||||
{
|
{
|
||||||
@@ -76,6 +121,10 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
|||||||
|
|
||||||
void Canvas::addControl(std::unique_ptr<Control> control)
|
void Canvas::addControl(std::unique_ptr<Control> control)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//坐标转化
|
||||||
|
control->setX(control->getLocalX() + this->x);
|
||||||
|
control->setY(control->getLocalY() + this->y);
|
||||||
control->setParent(this);
|
control->setParent(this);
|
||||||
controls.push_back(std::move(control));
|
controls.push_back(std::move(control));
|
||||||
dirty = true;
|
dirty = true;
|
||||||
@@ -155,9 +204,173 @@ void Canvas::setDirty(bool dirty)
|
|||||||
|
|
||||||
void Canvas::onWindowResize()
|
void Canvas::onWindowResize()
|
||||||
{
|
{
|
||||||
Control::onWindowResize(); // 先处理自己
|
// 首先处理自身的快照等逻辑
|
||||||
for (auto& ch : controls) // 再转发给所有子控件
|
Control::onWindowResize();
|
||||||
ch->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)
|
void Canvas::requestRepaint(Control* parent)
|
||||||
@@ -166,10 +379,7 @@ void Canvas::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();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
onRequestRepaintAsRoot();
|
onRequestRepaintAsRoot();
|
||||||
@@ -177,3 +387,5 @@ void Canvas::requestRepaint(Control* parent)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,27 @@ void Control::onWindowResize()
|
|||||||
discardBackground();
|
discardBackground();
|
||||||
setDirty(true);
|
setDirty(true);
|
||||||
}
|
}
|
||||||
|
void Control::setLayoutMode(StellarX::LayoutMode layoutMode_)
|
||||||
|
{
|
||||||
|
this->layoutMode = layoutMode_;
|
||||||
|
}
|
||||||
|
void Control::setAnchor(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()
|
void Control::saveStyle()
|
||||||
@@ -123,6 +144,7 @@ void Control::discardBackground()
|
|||||||
{
|
{
|
||||||
if (saveBkImage)
|
if (saveBkImage)
|
||||||
{
|
{
|
||||||
|
restBackground();
|
||||||
delete saveBkImage;
|
delete saveBkImage;
|
||||||
saveBkImage = nullptr;
|
saveBkImage = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
140
src/Dialog.cpp
140
src/Dialog.cpp
@@ -40,13 +40,7 @@ void Dialog::draw()
|
|||||||
Canvas::setLinewidth(BorderWidth);
|
Canvas::setLinewidth(BorderWidth);
|
||||||
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();
|
||||||
|
|
||||||
//绘制消息文本
|
//绘制消息文本
|
||||||
@@ -59,7 +53,7 @@ void Dialog::draw()
|
|||||||
|
|
||||||
|
|
||||||
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()));
|
||||||
@@ -170,33 +164,71 @@ void Dialog::Show()
|
|||||||
if (modal)
|
if (modal)
|
||||||
{
|
{
|
||||||
// 模态对话框需要阻塞当前线程直到对话框关闭
|
// 模态对话框需要阻塞当前线程直到对话框关闭
|
||||||
while (show && !close)
|
if (modal)
|
||||||
{
|
{
|
||||||
|
// 记录当前窗口客户区尺寸,供轮询对比
|
||||||
// 处理消息
|
RECT rc0;
|
||||||
ExMessage msg;
|
GetClientRect(hWnd.getHwnd(), &rc0);
|
||||||
if (peekmessage(&msg, EX_MOUSE | EX_KEY))
|
int lastW = rc0.right - rc0.left;
|
||||||
{
|
int lastH = rc0.bottom - rc0.top;
|
||||||
handleEvent(msg);
|
|
||||||
|
|
||||||
// 检查是否需要关闭
|
while (show && !close)
|
||||||
if (shouldClose)
|
{
|
||||||
|
// ① 轮询窗口尺寸(不依赖 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();
|
lastW = cw;
|
||||||
break;
|
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 (pendingCleanup && !isCleaning)
|
||||||
if (dirty)
|
performDelayedCleanup();
|
||||||
{
|
|
||||||
requestRepaint(parent);
|
|
||||||
FlushBatchDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 避免CPU占用过高
|
|
||||||
Sleep(10);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 非模态仍由主循环托管
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
// 模态对话框关闭后执行清理
|
// 模态对话框关闭后执行清理
|
||||||
if (pendingCleanup && !isCleaning)
|
if (pendingCleanup && !isCleaning)
|
||||||
performDelayedCleanup();
|
performDelayedCleanup();
|
||||||
@@ -231,6 +263,7 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,7 +550,7 @@ void Dialog::getTextSize()
|
|||||||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||||||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||||||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
||||||
for (auto text : lines)
|
for (auto& text : lines)
|
||||||
{
|
{
|
||||||
int w = textwidth(LPCTSTR(text.c_str()));
|
int w = textwidth(LPCTSTR(text.c_str()));
|
||||||
int h = textheight(LPCTSTR(text.c_str()));
|
int h = textheight(LPCTSTR(text.c_str()));
|
||||||
@@ -583,30 +616,11 @@ void Dialog::initDialogSize()
|
|||||||
initCloseButton(); // 初始化关闭按钮
|
initCloseButton(); // 初始化关闭按钮
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialog::saveBackground(int x, int y, int w, int h)
|
void Dialog::addControl(std::unique_ptr<Control> control)
|
||||||
{
|
{
|
||||||
if (w <= 0 || h <= 0) return;
|
control->setParent(this);
|
||||||
saveBkX = x; saveBkY = y; saveWidth = w; saveHeight = h;
|
controls.push_back(std::move(control));
|
||||||
if (saveBkImage)
|
dirty = true;
|
||||||
{
|
|
||||||
//尺寸变了才重建,避免反复 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 延迟清理策略:由于对话框绘制时保存了背景快照,必须在对话框隐藏后、
|
// 延迟清理策略:由于对话框绘制时保存了背景快照,必须在对话框隐藏后、
|
||||||
@@ -634,6 +648,23 @@ void Dialog::performDelayedCleanup()
|
|||||||
FlushBatchDraw();
|
FlushBatchDraw();
|
||||||
discardBackground();
|
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;
|
needsInitialization = true;
|
||||||
pendingCleanup = false;
|
pendingCleanup = false;
|
||||||
@@ -684,11 +715,8 @@ void Dialog::requestRepaint(Control* parent)
|
|||||||
if (this == parent)
|
if (this == parent)
|
||||||
{
|
{
|
||||||
for (auto& control : controls)
|
for (auto& control : controls)
|
||||||
if (control->isDirty()&&control->IsVisible())
|
if (control->isDirty() && control->IsVisible())
|
||||||
{
|
|
||||||
control->draw();
|
control->draw();
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -162,25 +162,68 @@ 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;
|
||||||
if ((saveBkX != this->x) || (saveBkY != this->y) || (!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height) || !saveBkImage)
|
// // 在绘制 TabControl 之前,先恢复并更新背景快照:
|
||||||
saveBackground(this->x, this->y, this->width, this->height);
|
//int margin = canvaslinewidth > 1 ? canvaslinewidth : 1;
|
||||||
// 恢复背景(清除旧内容)
|
//if (hasSnap)
|
||||||
restBackground();
|
//{
|
||||||
Canvas::draw();
|
// // 恢复旧快照,清除上一次绘制
|
||||||
|
// restBackground();
|
||||||
|
// // 如果位置或尺寸变了,或没有有效缓存,则重新抓取
|
||||||
|
// if (!saveBkImage || saveBkX != this->x - margin || saveBkY != this->y - margin || saveWidth != this->width + margin * 2 || saveHeight != this->height + margin * 2)
|
||||||
|
// {
|
||||||
|
// discardBackground();
|
||||||
|
// saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// // 首次绘制或没有快照时直接抓取背景
|
||||||
|
// saveBackground(this->x - margin, this->y - margin, this->width + margin * 2, this->height + margin * 2);
|
||||||
|
//}
|
||||||
|
// // 再次恢复最新背景,保证绘制区域干净
|
||||||
|
// restBackground();
|
||||||
|
// 绘制画布背景和基本形状及其子画布控件
|
||||||
|
Canvas::draw();
|
||||||
for (auto& c : controls)
|
for (auto& c : controls)
|
||||||
{
|
{
|
||||||
c.first->setDirty(true);
|
c.first->setDirty(true);
|
||||||
c.first->draw();
|
c.first->draw();
|
||||||
}
|
}
|
||||||
for (auto& c : controls)
|
for (auto& c : controls)
|
||||||
if(c.second->IsVisible())
|
{
|
||||||
{
|
c.second->setDirty(true);
|
||||||
c.second->setDirty(true);
|
c.second->draw();
|
||||||
c.second->draw();
|
}
|
||||||
}
|
|
||||||
dirty = false;
|
dirty = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -216,6 +259,7 @@ void TabControl::add(std::pair<std::unique_ptr<Button>, std::unique_ptr<Canvas>>
|
|||||||
controls[idx].first->setParent(this);
|
controls[idx].first->setParent(this);
|
||||||
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);
|
||||||
@@ -291,12 +335,19 @@ void TabControl::setIsVisible(bool visible)
|
|||||||
|
|
||||||
void TabControl::onWindowResize()
|
void TabControl::onWindowResize()
|
||||||
{
|
{
|
||||||
Control::onWindowResize();
|
// 调用基类的窗口变化处理,丢弃快照并标记脏
|
||||||
for (auto& c : controls)
|
Control::onWindowResize();
|
||||||
{
|
// 根据当前 TabControl 的新尺寸重新计算页签栏和页面区域
|
||||||
c.first->onWindowResize();
|
initTabBar();
|
||||||
c.second->onWindowResize();
|
initTabPage();
|
||||||
}
|
// 转发窗口尺寸变化给所有页签按钮和页面
|
||||||
|
for (auto& c : controls)
|
||||||
|
{
|
||||||
|
c.first->onWindowResize();
|
||||||
|
c.second->onWindowResize();
|
||||||
|
}
|
||||||
|
// 尺寸变化后需要重绘自身
|
||||||
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TabControl::getActiveIndex() const
|
int TabControl::getActiveIndex() const
|
||||||
@@ -364,15 +415,9 @@ void TabControl::requestRepaint(Control* parent)
|
|||||||
for (auto& control : controls)
|
for (auto& control : controls)
|
||||||
{
|
{
|
||||||
if (control.first->isDirty() && control.first->IsVisible())
|
if (control.first->isDirty() && control.first->IsVisible())
|
||||||
{
|
|
||||||
control.first->draw();
|
control.first->draw();
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (control.second->isDirty()&&control.second->IsVisible())
|
else if (control.second->isDirty()&&control.second->IsVisible())
|
||||||
{
|
|
||||||
control.second->draw();
|
control.second->draw();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
156
src/table.cpp
156
src/table.cpp
@@ -87,28 +87,35 @@ void Table::initTextWaH()
|
|||||||
if (h > maxLineH)
|
if (h > maxLineH)
|
||||||
maxLineH = h;
|
maxLineH = h;
|
||||||
|
|
||||||
// 列的像素宽 = 内容宽 + 左右 padding
|
// 列宽包含左右 padding:在计算完最大文本宽度后,加上 2*padX 作为单元格内边距
|
||||||
// 表内容总宽 = Σ(列宽 + 列间距)
|
for (size_t j = 0; j < colWidths.size(); ++j) {
|
||||||
int contentW = 0;
|
colWidths[j] += 2 * padX;
|
||||||
for (size_t j = 0; j < colWidths.size(); ++j)
|
}
|
||||||
contentW += (colWidths[j] + 2 * padX) + colGap;
|
|
||||||
|
|
||||||
// 表头高 & 行高(与 drawHeader/drawTable 内部一致:+上下 padding)
|
// 表内容总宽 = Σ(列宽 + 列间距)
|
||||||
const int headerH = maxLineH + 2 * padY;
|
int contentW = 0;
|
||||||
const int rowH = maxLineH + 2 * padY;
|
for (size_t j = 0; j < colWidths.size(); ++j)
|
||||||
const int rowsH = rowH * rowsPerPage;
|
contentW += colWidths[j] + colGap;
|
||||||
|
|
||||||
// 页脚:
|
// 表头高 & 行高(与 drawHeader/drawTable 内部一致:+上下 padding)
|
||||||
const int pageTextH = textheight(LPCTSTR(pageNumtext.c_str()));
|
const int headerH = maxLineH + 2 * padY;
|
||||||
const int btnTextH = textheight(LPCTSTR("上一页"));
|
const int rowH = maxLineH + 2 * padY;
|
||||||
const int btnPadV = TABLE_BTN_TEXT_PAD_V;
|
const int rowsH = rowH * rowsPerPage;
|
||||||
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);
|
const int pageTextH = textheight(LPCTSTR(pageNumtext.c_str()));
|
||||||
this->height = headerH + rowsH + footerH + (border << 1);
|
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()
|
void Table::initButton()
|
||||||
@@ -171,6 +178,7 @@ void Table::initButton()
|
|||||||
if (pageNum) pageNum->setDirty(true);
|
if (pageNum) pageNum->setDirty(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
isNeedButtonAndPageNum = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Table::initPageNum()
|
void Table::initPageNum()
|
||||||
@@ -214,7 +222,7 @@ void Table::drawPageNum()
|
|||||||
pageNumtext += "页/共";
|
pageNumtext += "页/共";
|
||||||
pageNumtext += std::to_string(totalPages);
|
pageNumtext += std::to_string(totalPages);
|
||||||
pageNumtext += "页";
|
pageNumtext += "页";
|
||||||
if (nullptr == pageNum)
|
if (nullptr == pageNum || isNeedButtonAndPageNum)
|
||||||
initPageNum();
|
initPageNum();
|
||||||
pageNum->setText(pageNumtext);
|
pageNum->setText(pageNumtext);
|
||||||
pageNum->textStyle = this->textStyle;
|
pageNum->textStyle = this->textStyle;
|
||||||
@@ -226,7 +234,7 @@ void Table::drawPageNum()
|
|||||||
|
|
||||||
void Table::drawButton()
|
void Table::drawButton()
|
||||||
{
|
{
|
||||||
if (nullptr == prevButton || nullptr == nextButton)
|
if ((nullptr == prevButton || nullptr == nextButton)|| isNeedButtonAndPageNum)
|
||||||
initButton();
|
initButton();
|
||||||
|
|
||||||
this->prevButton->textStyle = this->textStyle;
|
this->prevButton->textStyle = this->textStyle;
|
||||||
@@ -242,6 +250,58 @@ void Table::drawButton()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 调整列宽以匹配新的表格总宽度。不修改 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)
|
Table::Table(int x, int y)
|
||||||
:Control(x, y, 0, 0)
|
:Control(x, y, 0, 0)
|
||||||
{
|
{
|
||||||
@@ -317,13 +377,27 @@ void Table::draw()
|
|||||||
setfillstyle((int)tableFillMode);
|
setfillstyle((int)tableFillMode);
|
||||||
setbkmode(TRANSPARENT);
|
setbkmode(TRANSPARENT);
|
||||||
}
|
}
|
||||||
//确保在绘制任何表格内容之前捕获背景
|
// 在绘制前先恢复并更新背景快照:
|
||||||
// 临时恢复样式,确保捕获正确的背景
|
// 如果已有快照且尺寸发生变化,先恢复旧快照以清除上一次绘制,然后丢弃旧快照再重新抓取新的区域。
|
||||||
if ((!hasSnap) || (saveWidth != this->width) || (saveHeight != this->height)||!saveBkImage)
|
if (hasSnap)
|
||||||
saveBackground(this->x, this->y, this->width, this->height);
|
{
|
||||||
// 恢复背景(清除旧内容)
|
// 始终先恢复旧背景,清除上一帧内容
|
||||||
restBackground();
|
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;
|
dX = x;
|
||||||
dY = y;
|
dY = y;
|
||||||
@@ -365,7 +439,7 @@ bool Table::handleEvent(const ExMessage& msg)
|
|||||||
void Table::setHeaders(std::initializer_list<std::string> headers)
|
void Table::setHeaders(std::initializer_list<std::string> headers)
|
||||||
{
|
{
|
||||||
this->headers.clear();
|
this->headers.clear();
|
||||||
for (auto lis : headers)
|
for (auto& lis : headers)
|
||||||
this->headers.push_back(lis);
|
this->headers.push_back(lis);
|
||||||
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
|
isNeedCellSize = true; // 标记需要重新计算单元格尺寸
|
||||||
isNeedDrawHeaders = true; // 标记需要重新绘制表头
|
isNeedDrawHeaders = true; // 标记需要重新绘制表头
|
||||||
@@ -464,6 +538,17 @@ void Table::setTableBorderWidth(int width)
|
|||||||
this->dirty = true;
|
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
|
int Table::getCurrentPage() const
|
||||||
{
|
{
|
||||||
return this->currentPage;
|
return this->currentPage;
|
||||||
@@ -519,4 +604,17 @@ int Table::getTableBorderWidth() const
|
|||||||
return this->tableBorderWidth;
|
return this->tableBorderWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Table::getTableWidth() const
|
||||||
|
{
|
||||||
|
int temp = 0;
|
||||||
|
for (auto& w : colWidths)
|
||||||
|
temp += w;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Table::getTableHeight() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,12 @@ bool TextBox::handleEvent(const ExMessage& msg)
|
|||||||
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];
|
||||||
consume = true;
|
dirty = InputBox(temp, (int)maxCharLen, "输入框", NULL, text.c_str(), NULL, NULL, false);
|
||||||
|
if(dirty)text = temp;
|
||||||
|
delete[] temp;
|
||||||
|
temp = nullptr;
|
||||||
|
consume = true;
|
||||||
}
|
}
|
||||||
else if (StellarX::TextBoxmode::READONLY_MODE == mode)
|
else if (StellarX::TextBoxmode::READONLY_MODE == mode)
|
||||||
{
|
{
|
||||||
|
|||||||
894
src/window.cpp
894
src/window.cpp
File diff suppressed because it is too large
Load Diff
2651
控件 API 文档 (StellarX GUI Framework Controls API Documentation).md
Normal file
2651
控件 API 文档 (StellarX GUI Framework Controls API Documentation).md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user