BayTemplate/CLAUDE.md

646 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
BayTemplate is a **Bay Template Designer of Grid Framework DesignTime** tool - a Qt-based application for designing electrical power grid structures. It features a dockable UI with a graphics canvas for placing and manipulating grid elements.
## Build Commands
### Build the Project
使用 CMake presets 进行配置和构建:
```bash
# Configure using preset
cmake --preset default
# Build with Ninja
ninja -C build/Debug
```
### Output Location
- Executable: `build/Debug/BayTemplate.app` (macOS)
## Architecture
### Core Components
#### 1. Docking System (Qt Advanced Docking System 4.3.1)
- **CDockManager** manages dockable panels
- **Left Dock**: 图元面板 (GraphicElementsPanel) - library of draggable grid elements
- **Center**: DrawingPanel - main canvas area with DesignerScene
- **Right Dock**: 属性编辑器 (PropertyEditor) - QDetailsView for editing selected item properties
#### 2. Graphics View Framework
- **DesignerView** (`QGraphicsView` subclass): Custom view with zoom (0.02-50x), pan (middle-button drag), checkerboard background
- **DesignerScene** (`QGraphicsScene` subclass): Custom scene with grid overlay (20px spacing), routes mouse events through SelectorManager
#### 3. Selector System
Selector hierarchy in `include/util/`:
- **BaseSelector**: Abstract base for all selectors
- **CreatingSelector**: Mode for creating new items from template
- **MovingSelector**: Mode for moving items
- **RotationSelector**: Mode for rotating items around origin
- **ScalingSelector**: Mode for scaling items
- **EditingSelector**: Mode for editing polygon vertex positions
- **SelectorManager**: Singleton that manages the active working selector
Mouse events flow: `DesignerScene``SelectorManager::getWorkingSelector()` → specific selector implementation
#### 4. Graphics Items (`include/graphicsItem/`)
- **GraphicsBaseItem**: Abstract base class for all grid elements
- **GraphicsRectItem**: Rectangle element
- **GraphicsPolygonItem**: Polygon element (editable vertices)
- **GraphicsItemGroup**: Container for grouped items (supports flattening to prevent nesting)
- **GraphicsBusSectionItem**: Bus section element
- **ItemControlHandle**: Visual handles for manipulation (rotation, scaling, editing)
#### 5. Property Editor (Qt PropertyEditor)
- **QDetailsView**: Qt Quick-based property editor in right dock
- **QCustomType**: Custom property type for graphics item properties
- **PropertyTypeCustomization_CustomType**: Custom property editor integration
- Property editor observes `QGraphicsScene` and displays properties of selected items
#### 6. Command Pattern (Undo/Redo)
- **QUndoStack** (`m_pUndoStack` in CMainWindow) manages undo/redo
- **OperationCommand**: Base command class
- **AddItemCommand**: Add item to scene
- **DeleteItemCommand**: Remove item from scene
- **CreateItemGoupCommand**: Group selected items (with flattening)
- **DestroyItemGoupCommand**: Ungroup items
### Signal Flow
1. **User drags from 图元面板**`GraphicElementsPanel` emits signal → `DesignerScene::signalAddItem()` → creates item with `AddItemCommand`
2. **User clicks on canvas**`DesignerView``DesignerScene``SelectorManager::getWorkingSelector()` → selector processes event
3. **User selects items**`DesignerScene::selectionChanged()``CMainWindow::onSignal_selectionChanged()` → updates property editor
4. **User modifies property in 属性编辑器**`QDetailsView` updates item property → scene updates
### Key Files and Relationships
```
source/main.cpp # Entry point - creates CMainWindow
└── source/mainwindow.h/.cpp # Main window with CDockManager, initializes all components
├── initializeDockUi() # Sets up docks (left, center, right)
├── initializeAction() # Sets up QUndoStack and menu actions
├── initializeDocument() # Creates Document, associates with DesignerScene
├── Event handlers # onSignal_addItem, onSignal_selectionChanged, etc.
└── File operations # onAction_new(), onAction_open(), onAction_save()
source/document.h/.cpp # Document class - serialization and state management
├── serialize()/deserialize() # Core JSON serialization
├── saveToFile()/loadFromFile() # File I/O operations
└── State management # filename, modified, timestamps, meta data
source/drawingPanel.h/.cpp # Central dock widget containing DesignerView
└── DesignerView + DesignerScene
source/designerView.h/.cpp # QGraphicsView subclass - zoom, pan, middle-button navigation
source/designerScene.h/.cpp # QGraphicsScene subclass - grid background, group operations
└── Delegates to SelectorManager for mouse events
source/util/selectorManager.h/.cpp # Singleton managing active selector
└── Selector::mousePressEvent/ReleaseEvent/MoveEvent()
source/graphicsItem/ # Graphics item implementations
├── GraphicsBaseItem # Base class with common functionality
├── GraphicsRectItem # Rectangle
├── GraphicsPolygonItem # Editable polygon
├── GraphicsItemGroup # Item grouping
└── GraphicsBusSectionItem
source/propertyType/ # Custom property editor integration
├── CustomType.h
├── CustomGadget.h
└── PropertyTypeCustomization_CustomType.cpp
```
### Third-Party Dependencies
1. **QtADS** (`QtADS/` subdirectory, v4.3.1): Advanced docking system - must be present as subdirectory
2. **PropertyEditor** (`PropertyEditor/` subdirectory): Qt Quick-based property editing system with QDetailsView
Both are added via `add_subdirectory()` in CMakeLists.txt and must exist in the repository root.
### Platform Support
- **Qt Version**: Qt5 or Qt6 (auto-detected, `find_package(QT NAMES Qt6 Qt5 ...)`)
- **Architectures**: x86, x64, arm64, aarch64 (auto-detected in CMakeLists.txt)
- **Platforms**: Windows, macOS, Linux, Android (Android uses shared library)
### Important Implementation Details
1. **CMAKE_AUTOUIC_SEARCH_PATHS**: Set to `"ui"` in CMakeLists.txt because .ui files are in separate directory from header files
2. **Group Flattening**: When creating a group that contains existing groups, the scene flattens nested groups first to prevent group nesting (see `DesignerScene::createGroup()`)
3. **Zoom Implementation**: Uses `QGraphicsView::zoom()` with smooth factor `qPow(1.0015, angleDelta.y())` per wheel event
4. **Wchar String Conversion**: Uses `QString::fromWCharArray(L"中文")` for Chinese UI strings
5. **Resource File**: `resource/BayTemplate.qrc` contains checkerboard background and icons, referenced in .ui files and CMakeLists.txt
---
## Graphics View Architecture Deep Dive
### MVC/MVVM Architecture Mapping
BayTemplate implements a classic **Model-View-Controller** pattern using Qt's Graphics View Framework:
| Layer | Qt Graphics View | BayTemplate Implementation | Responsibility |
|-------|------------------|---------------------------|----------------|
| **Model** | QGraphicsItem | GraphicsBaseItem, GraphicsRectItem, GraphicPolygonItem, GraphicsItemGroup | 数据模型存储图元几何数据、样式属性pen/brush、变换信息位置/旋转/缩放)|
| **Model Manager** | QGraphicsScene | DesignerScene | 模型管理:管理 item 生命周期、选择状态、碰撞检测、背景绘制(网格)|
| **View** | QGraphicsView | DesignerView | 视图呈现:坐标系变换、滚动条控制、鼠标事件捕获、缩放/平移 |
| **Controller** | (Qt 无内置) | SelectorManager + BaseSelector 体系 | 控制器:解释用户输入,转换为模型操作(创建/移动/旋转/缩放/编辑)|
**关键点:**
- Qt 的 Graphics View 框架提供了 MVC 中的 Model 和 View 层
- Controller 层需要开发者自行实现BayTemplate 使用 SelectorManager 作为中枢
- DesignerScene 介于 Model Manager 和 Controller 之间,负责事件分发
### View-Scene-Item 三层架构详解
```
┌─────────────────────────────────────────────────────────────┐
│ DesignerView │
│ (Viewport / Presentation) │
├─────────────────────────────────────────────────────────────┤
│ • 坐标变换Viewport Coordinates ↔ Scene Coordinates │
│ • 缩放控制0.02x - 50x (mouse wheel with smooth zoom) │
│ • 平移控制Middle-button drag (translate transform) │
│ • 事件捕获mousePressEvent/moveEvent/releaseEvent │
│ • 无滚动条setHorizontal/VerticalScrollBarPolicy(Off) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ DesignerScene │
│ (Scene Graph Manager) │
├─────────────────────────────────────────────────────────────┤
│ • Item 管理addItem(), removeItem(), selectedItems() │
│ • 背景绘制drawBackground() 绘制 20px 间距的藏青色虚线网格 │
│ • 事件分发:将鼠标事件转发给 SelectorManager │
│ • 组操作createGroup() 实现扁平化打组 │
│ • 信号发射signalAddItem(), selectionChanged() │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ GraphicsItem │
│ (Data Model) │
├─────────────────────────────────────────────────────────────┤
│ GraphicsBaseItem (AbstractShapeType<QGraphicsItem>) │
│ ├── GraphicsRectItem (rectangle, 圆角矩形) │
│ ├── GraphicPolygonItem (多点绘制,顶点编辑) │
│ ├── GraphicsBusSectionItem (总线段) │
│ └── GraphicsItemGroup (AbstractShapeType<QGroup>) │
└─────────────────────────────────────────────────────────────┘
```
### 坐标系与变换系统
BayTemplate 使用多层坐标系,理解它们对添加 Document 类至关重要:
```
1. Viewport Coordinates (像素坐标)
└── DesignerView 的 viewport 矩形,(0,0) 在左上角
└── 受窗口大小、缩放影响
2. Scene Coordinates (场景坐标)
└── 全局坐标系,(0,0) 为场景原点
└── 通过 mapToScene()/mapFromScene() 与 Viewport 互转
└── 图元的位置 pos() 使用场景坐标
3. Item Coordinates (图元局部坐标)
└── 每个 item 的局部坐标系,原点在 (0,0)
└── BayTemplate 中,图元中心点通常与 (0,0) 重合
└── boundingRect() 返回局部坐标中的边界
4. Parent Coordinates (父项坐标)
└── 当 item 加入 group 后,使用 group 的局部坐标
└── mapToParent()/mapFromParent() 用于转换
```
**变换链Transform Chain**
```
Item Coordinates → Item Transform → Parent Coordinates → ... → Scene Coordinates → View Transform → Viewport Coordinates
```
- **Item Transform**: 由 `setPos()`, `setRotation()`, `setScale()` 控制的 3×3 变换矩阵
- **Transform Origin**: `setTransformOriginPoint()` 设置变换中心BayTemplate 中通常为中心点)
- **View Transform**: DesignerView 的 zoom/pan 产生的变换,影响所有 item 的显示
**关键函数:**
- `item->mapToScene(point)`: Item 局部坐标 → 场景坐标
- `item->mapToView(point)`: Item 局部坐标 → Viewport 坐标
- `view->mapToScene(point)`: Viewport 坐标 → 场景坐标
- `view->mapSceneToViewport(rect)`: 场景矩形 → Viewport 矩形
### 操作模式与状态机Selector 系统)
SelectorManager 实现了一个**状态机**,不同的 Selector 代表不同的操作模式:
```
┌─────────────────┐
│ ST_base │
│ (空闲状态) │
└────────┬────────┘
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ST_creating │ │ ST_moving │ │ ST_editing │
│ (创建模式) │ │ (移动模式) │ │ (顶点编辑) │
└──────────────┘ └──────────────┘ └──────────────┘
│ │
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ST_scaling │ │ ST_rotation │
│ (缩放模式) │ │ (旋转模式) │
└──────────────┘ └──────────────┘
│ │
└────────┬─────────┘
┌────────┐
│ ST_base│ (操作完成后返回)
└────────┘
```
**状态转换规则:**
| 触发条件 | 从状态 | 到状态 | 说明 |
|---------|--------|--------|------|
| 从图元面板拖拽 | ST_base | ST_creating | 设置 CreatingSelector 的 m_pCreatingItem |
| 点击选中 item左键 | ST_base | ST_moving/ST_scaling/ST_rotation | 根据是否点击在 handle 上判断 |
| 点击空白处 | 任何 | ST_base | 取消选择,禁用 RubberBandDrag |
| mouseReleaseEvent | ST_moving/scaling/rotation | ST_base | 操作完成,返回空闲状态 |
**BaseSelector 静态成员(所有 Selector 共享):**
```cpp
static QPointF ms_ptMouseDown; // 鼠标按下位置(场景坐标)
static QPointF ms_ptMouseLast; // 鼠标当前位置(场景坐标)
static double ms_dAngleMouseDownToItem; // 按下时鼠标与 item 中心的夹角(弧度→角度)
static int ms_nDragHandle; // 当前拖拽的 handle 类型
```
### Handle 控制系统
ItemControlHandle 是围绕在选中 item 周围的视觉控件,用于精细操作:
```
H_leftTop ──── H_top ──── H_rightTop
│ │ │
│ │ │
┌────┴────────────┴────────────┴────┐
│ │
│ Item Bounding │
│ Rect │
│ │
└────┬────────────┬────────────┬────┘
│ │ │
H_left ────── H_bottom ──── H_rightBottom
```
**Handle 类型枚举:**
```cpp
enum HandleTag {
H_none = 0, // 无 handle
H_leftTop, // 左上角(缩放)
H_top, // 上边中点(缩放)
H_rightTop, // 右上角(缩放)
H_right, // 右边中点(缩放)
H_rightBottom, // 右下角(缩放)
H_bottom, // 下边中点(缩放)
H_leftBottom, // 左下角(缩放)
H_left, // 左边中点(缩放)
H_rotate_leftTop, // 左上角外侧(旋转)
H_rotate_rightTop, // 右上角外侧(旋转)
H_rotate_rightBottom, // 右下角外侧(旋转)
H_rotate_leftBottom, // 左下角外侧(旋转)
H_edit, // 自定义编辑点(多边形顶点、圆角控制点)
// ... 更多编辑点
};
```
**Handle 工作机制:**
1. `AbstractShapeType::updateHandles()` 根据 boundingRect 计算 handle 位置
2. Handle 位置在 boundingRect 外扩 5px (nMargin = 5)
3. `collidesWithHandle()` 检测鼠标点击在哪个 handle 上
4. 不同操作模式对 handle 的响应不同
### 操作副本Operation Copy模式
移动和旋转操作使用"预览副本"技术,提供视觉反馈:
```cpp
// 1. mousePressEvent: 创建副本
void createOperationCopy() {
m_pOperationCopy = new QGraphicsPathItem(this->shape());
m_pOperationCopy->setPen(Qt::DashLine); // 虚线表示预览
m_pOperationCopy->setPos(this->pos());
scene->addItem(m_pOperationCopy);
m_movingIniPos = this->pos();
}
// 2. mouseMoveEvent: 移动副本(本体不动)
void moveOperationCopy(const QPointF& delta) {
m_pOperationCopy->setPos(m_movingIniPos + delta);
}
// 3. mouseReleaseEvent: 副本位置应用到本体
void removeOperationCopy() {
this->setPos(m_pOperationCopy->pos()); // 本体移动到副本位置
scene->removeItem(m_pOperationCopy);
delete m_pOperationCopy;
}
```
**优势:**
- 移动过程中保持原来的 selection state
- 可以拖出 boundingRect_selected 范围(否则会被误认为取消选择)
- 旋转/缩放同理
### 图元基类架构AbstractShapeType 模板)
```cpp
// 使用模板继承,允许继承 QGraphicsItem 或 QGraphicsItemGroup
template <typename BaseType = QGraphicsItem>
class AbstractShapeType : public BaseType {
protected:
ShapeType m_type; // T_undefined/T_item/T_group
QPen m_pen; // 画笔(边框)
QBrush m_brush; // 画刷(填充)
double m_dWidth, m_dHeight; // 宽高
QRectF m_boundingRect; // 局部坐标中的边界矩形
QRectF m_boundingRect_selected; // 选中框(外扩 5px
double m_dSyncRotationByParent; // 父项旋转同步数据
QVector<ItemControlHandle*> m_vecHanle;
QGraphicsPathItem* m_pOperationCopy;
QPointF m_movingIniPos;
};
// 具体类型定义
typedef AbstractShapeType<QGraphicsItem> AbstractShape; // 单图元基类
// GraphicsBaseItem : QObject, AbstractShape
// GraphicsItemGroup : QObject, AbstractShapeType<QGraphicsItemGroup> // 组图元基类
```
**关键点:**
- `m_boundingRect` 的中心点与 item 的 (0,0) 原点重合,简化变换计算
- `updateCoordinate()` 在 resize/editShape 后重新校准原点
- `m_dSyncRotationByParent` 用于组内 item 跟踪父组的旋转(因为 item 的 rotation() 不会自动更新)
### 命令模式Command Pattern
所有可撤销操作都通过 QUndoCommand 实现:
```cpp
class AddItemCommand : public QUndoCommand {
void undo() override { scene->removeItem(m_pItem); }
void redo() override {
if(!m_pItem->scene()) scene->addItem(m_pItem);
}
};
class DeleteItemCommand : public QUndoCommand {
void undo() override {
foreach(item, m_listItem) scene->addItem(item);
}
void redo() override {
foreach(item, m_listItem) scene->removeItem(item);
}
};
```
**关键点:**
- `QUndoStack::push(command)` 会自动调用 `command->redo()`
- `DeleteItemCommand` 使用 `removeItem()` 而非 `delete`,确保 undo 能恢复
- 命令对象持有 item 的引用(非所有权)
### 组Group扁平化策略
`DesignerScene::createGroup()` 实现组扁平化:
```cpp
// 如果选中的 item 中包含已有的 group先解散该 group将子项加入新组
foreach (item, selectedItems) {
if (item->getType() == T_group) {
GraphicsItemGroup* group = qgraphicsitem_cast<GraphicsItemGroup*>(item);
foreach (child, group->childItems()) {
listItem.push_back(child); // 将子项加入新列表
}
removeItem(group);
delete group; // 删除旧组
}
}
// 创建新组,避免嵌套
```
**目的:**
- 简化场景图结构(无嵌套的树)
- 简化坐标计算和碰撞检测
- 统一 undo/redo 逻辑
---
## Document 类架构(持久化层)
### Document 类在 MVC 中的位置
Document 类作为**持久化层**插入现有架构,位于 Model 层之下:
```
┌─────────────────────────────────────────┐
│ Controller │
│ (SelectorManager + 各种 Selector) │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ View │
│ (DesignerView + UI) │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Model Manager │
│ (DesignerScene) │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Model │
│ (GraphicsItem 层次结构) │
└──────────────┬──────────────────────────┘
┌──────────────▼──────────────────────────┐
│ Document │
│ (序列化/状态管理) │
└──────────────────────────────────────────┘
```
**设计原则:**
- **组合优于继承**Document 持有 `DesignerScene*` 弱引用,不继承 Scene
- **单向依赖**Document 只依赖 DesignerScene 的接口,不依赖 CMainWindow
- **信号槽通信**:通过信号与外部通信修改状态,避免紧耦合
### Document 类职责
1. **序列化/反序列化**JSON 格式)
- `serialize()` / `deserialize()`: 核心序列化为 QByteArray
- `saveToFile()` / `loadFromFile()`: 文件 I/O 封装
- 递归序列化/反序列化图元层次结构(支持组图元嵌套)
2. **状态管理**
- `m_filename`: 文档文件名(空表示未保存的新文档)
- `m_modified`: 是否被修改(通过 `setModified()``modifiedChanged` 信号通知)
- `m_created` / `m_modifiedTime` / `m_lastSavedTime`: 时间戳
- `m_metaData`: 可扩展的自定义元数据 (`QMap<QString, QVariant>`)
3. **与 CMainWindow 的集成**
- `initializeDocument()`: 创建 Document 并关联 DesignerScene
- 连接 `modifiedChanged` 信号来更新窗口标题(添加 * 标记)
- `closeEvent()` 中检查 `isModified()` 提示用户保存
- 文件操作New/Open/Save 通过 Document 完成
### 序列化格式v1.0
```json
{
"version": "1.0",
"created": "2024-01-01T12:00:00",
"modified": "2024-01-01T14:30:00",
"filename": "/path/to/file.bay",
"metaData": {
"customKey": "customValue"
},
"items": [
{
"type": "GraphicsRectItem",
"id": "140737488320",
"pos": {"x": 100.0, "y": 200.0},
"rotation": 45.0,
"scale": {"x": 1.0, "y": 1.0},
"pen": {"color": "#000000", "width": 1.0, "style": 0},
"brush": {"color": "#FF0000"},
"width": 100.0,
"height": 50.0,
"properties": {},
"children": []
},
{
"type": "GraphicsItemGroup",
"pos": {"x": 300.0, "y": 150.0},
"rotation": 0.0,
"scale": {"x": 1.0, "y": 1.0},
"width": 200.0,
"height": 100.0,
"children": [
{ "type": "GraphicsRectItem", ... },
{ "type": "GraphicsBusSectionItem", ... }
]
}
]
}
```
### 实现细节
**类型识别:** 使用 `qobject_cast<T*>(item)` 配合 `getTypeString()` 函数识别图元类型(要求类型有 `Q_OBJECT` 宏)
**QVariant ↔ QJsonValue 转换:** 提供 `toJsonValue()` / `fromJsonValue()` 辅助函数处理元数据序列化
**坐标系统:** 图元使用中心原点,`pos()` 存储场景坐标,`boundingRect()` 使用局部坐标
**文件扩展名:** `.bay` 作为 BayTemplate 专用格式
### 关键文件
- `include/document.h`: Document 类声明
- `source/document.cpp`: 序列化/反序列化实现
- `source/mainwindow.cpp`: `initializeDocument()`, `onAction_new()`, `onAction_open()`, `onAction_save()`
---
## Selector 系统设计模式总结
### 状态模式State Pattern
SelectorManager 实现状态模式,不同的 Selector 子类代表不同的状态:
```cpp
// Context
class SelectorManager {
BaseSelector* getWorkingSelector(); // 返回当前状态的 Selector
void setWorkingSelector(SelectorType);
};
// State 接口
class BaseSelector {
virtual void mousePressEvent(...) = 0;
virtual void mouseMoveEvent(...) = 0;
virtual void mouseReleaseEvent(...) = 0;
};
// Concrete State
class MovingSelector : public BaseSelector { ... };
class ScalingSelector : public BaseSelector { ... };
// ...
```
**静态成员共享的原因:**
BaseSelector 的静态成员ms_ptMouseDown 等)在所有 Selector 实例间共享,使得状态切换时无需传递上下文数据。
### 操作副本模式Operation Preview Pattern
移动、旋转、缩放都使用"操作副本"作为视觉预览:
1. mousePressEvent: 创建虚线副本
2. mouseMoveEvent: 移动副本(本体不动)
3. mouseReleaseEvent: 将副本的变换应用到本体,删除副本
### Handle 系统Manipulator Pattern
ItemControlHandle 实现操纵器模式,将抽象的变换操作(缩放、旋转)映射到可视化的交互控件。
---
## 坐标系转换速查表
| 转换 | 函数 | 说明 |
|------|------|------|
| Item → Scene | `item->mapToScene(point)` | 局部坐标转场景坐标 |
| Scene → Item | `item->mapFromScene(point)` | 场景坐标转局部坐标 |
| Item → Viewport | `item->mapToView(point)` | 局部坐标转屏幕像素 |
| Item → Parent | `item->mapToParent(point)` | 局部坐标转父项坐标 |
| Viewport → Scene | `view->mapToScene(point)` | 屏幕像素转场景坐标 |
| Scene Rect → Viewport Rect | `view->mapSceneToViewport(rect)` | 场景矩形转屏幕矩形 |
## Handle 类型速查表
| Handle 类型 | 位置 | 功能 |
|------------|------|------|
| H_leftTop ~ H_left | boundingRect 八方向 | 缩放 |
| H_rotate_leftTop ~ H_rotate_leftBottom | boundingRect 外扩 | 旋转 |
| H_edit + N | 自定义位置 | 形状编辑(多边形顶点、圆角控制点)|
## 操作模式速查表
| SelectorType | 触发方式 | 用途 |
|-------------|---------|------|
| ST_base | 默认 | 空闲状态,允许橡胶框选择 |
| ST_creating | 从图元面板拖拽 | 创建新图元 |
| ST_moving | 点击 item 主体 | 移动选中项 |
| ST_scaling | 点击缩放 handle | 缩放选中项 |
| ST_rotation | 点击旋转 handle | 旋转选中项 |
| ST_editing | 点击编辑 handle | 编辑多边形顶点等 |