Commit Graph
9 Commits
Author SHA1 Message Date
DeepSeek V4.1 Flash ab19a6e72e feat(UI): 显示布局边界(Android 风格)+ 工具栏设置菜单(齿轮)+ 三处可见收益优化
## 1. 新功能:齿轮按钮 -> 应用设置菜单 -> 显示布局边界

- ToolbarAction::SETTINGS 位于「导入文件」左侧;工具栏布局改为
  「左侧固定簇(kLeftClusterCount = 2:齿轮 + 导入文件)+ 右侧按钮组」。
- 齿轮是矢量化绘制(8 齿 + 外环 + 轴孔,全部按 half 的比例推导),
  不用字体字符「⚙」——该字符在不同回退字体里大小与基线不一致,会忽大忽小、垂直不居中。
  UiButtonWidget 新增 UiButtonIcon(NONE / GEAR)与 SetIcon。
- MenuScope::APP_SETTINGS(新增)+ 设置菜单:HEADER「调试」+ 勾选切换项「显示布局边界」
  (keepOpen:点击后菜单保持打开,勾选态由 UpdateItemLabel 回写)。
  锚点取 Toolbar::ButtonRect(SETTINGS) 的控件实测矩形,宿主不再自己推算按钮几何。

## 2. 布局边界:基类给协议,组件自己上报

- UiComponent 新增 virtual PaintDebugBounds(g, scale),默认只画 Area()。
- Toolbar / TabBar 覆盖:跳过与区域重合的根控件,遍历 m_root->Children()(层级 1);
  TabBar 另上报每个 ✕(层级 2)。
- SignalPanel 覆盖:表头 / 筛选框 / 列表 / 样式区(1)+ 滚动条、样式行、当前可见列表行(2)。
- PlotView 覆盖:每个 LaneVisual::rect(1);不上报随数据变化的瞬态几何(曲线/胶囊/提示)。
- UiWidget 新增 PaintDebugBounds(g, scale, depth) 递归整棵控件树 —— 已上树的控件零成本获得边界。
- 宿主 PaintLayoutBounds 补客户区外框、分隔条、打开的菜单/拾色器。

视觉与实现约束(Android「Show layout bounds」风):
- 只描边、不填充(几十个嵌套矩形叠加填充会把内容糊住);
- 层级配色 青蓝(2px) / 品红 / 琥珀 / 绿(1px);
- 线宽按设备像素固定(不乘 scale),临时 PixelOffsetModeNone + 关抗锯齿 + 整像素对齐,
  保证 1px 就是 1px;
- ⚠️ 只在 Render 最后一步叠加,绝不进入任何位图缓存(PlotView 4 层缓存 / SignalPanel 行位图缓存),
  否则关掉开关后线条会残留;
- ⚠️ fullFrame 并入 m_showLayoutBounds:开启时必须整帧回送,否则「只回送绘图区」的快路径
  会让装饰区边界停在旧帧上。

## 3. 可见收益优化(行为不变)

- Toolbar::Layout:控件树改为按需重建(m_treeDirty),只在规格/字体/主题/缩放/动态文本
  变化时重建。拖动面板分隔条时 Relayout 每帧调用一次 -> 不再每帧 10 次堆分配 + 8 次测量缓存失效。
- Toolbar::SetActionLabel:只写 m_labelOverrides(唯一权威)+ 置树脏,消除「覆盖表与控件文本
  双写」可能不同步的问题。
- UiWidget::DispatchRedraw(event):新增;收敛 Toolbar / TabBar 里重复的
  Dispatch(); return UiRectHasArea(TakeDirty());(TakeDirty 会清零,漏一次就把下一次真实变化
  误判成「无变化」)。

## 4. 验证

- make release / make debug:零 error 零 warning;TargetCheck.exe 897,024 B(< 900 KB)。
- 新增离屏探针 .preflight/lbcheck:21 / 21 PASS
  (齿轮在导入文件左侧/同高/更窄;开启后青蓝 24,930 px、品红 19,734 px;关闭后逐像素还原;
   菜单 scope、锚定贴合按钮、宽度未被撑大、条目初始未勾选、点击翻转开关、菜单保持打开)。
- Tools/rendercheck:8 个场景的「工具栏带以下区域」哈希与改造前**逐一相同**
  (证明改动像素被完全限制在工具栏带内);整帧哈希因新增按钮整体变化,属预期。
  FUNC 断言 7 / 7 PASS。
- Tools/uicheck:69 passed / 0 failed。

## 5. 文档

Documents/UI_Refactor_Plan.md / UI_Refactor_Progress.md / UI_Refactor_Report.md 追加第六阶段;
Documents/agent_mem.md 同步变更日志与「2026-10-28 第八轮」硬约束与验证坑。
2026-10-28 15:46:45 +08:00
DeepSeek V4.1 Flash 0ec8055e95 重构(UI): 统一组件协议 + 去手写链路 + 大文件按职责拆分 + 代码审查 + 剥离 Tools
## 1. 新增 UiComponent 顶层组件协议(不是负优化)

Components/Shared/Inc/UiComponent.hpp:纯接口、无状态,8 个方法
SetScale / Layout / Area / Paint / HitTest / OnMouseLeave / IsEditing / QueryCursor。

Toolbar / TabBar / SignalPanel / PlotView 本来就各自拥有完全同名同签名的成员,
因此改造 = 加 override + 补 Area()(PlotView 另加一个非 const 的 QueryCursor 转发,
因为它自己的查询会惰性建立行缓存),实现代码零改动、静态派发、体积无增长。

刻意不把 UiWidget 当作它的基类:UiWidget 是"树节点"(父给尺寸、父下探脏区、
命中沿树链派发),而 PlotView(4 层位图缓存)/ SignalPanel(行位图缓存)是
"地盘宿主"。强行上树会推翻"父脏则子全脏"的前提,历史上正是这类耦合导致过
"后台渲染覆盖平移结果""命中矩形错位"的事故。头文件里已写明这条理由。
Toolbar/TabBar 是两者的交集:对外实现 UiComponent,对内持有一棵 UiWidget 树。

## 2. 宿主侧:四条手写链 -> 组件表

Application 新增 ComponentEntry{ component, target, chrome } 与 m_components
(底->顶:plot -> panel -> toolbar -> tabBar),BuildComponentTable() 装配一次。

- ApplyDpi        6 行逐个 SetScale  ->  1 个循环
- Relayout        手算三列 x 坐标    ->  UiStackRects(内容区, HORIZONTAL, {panelW, splitW, AUTO})
- WM_MOUSELEAVE   逐个 OnMouseLeave   ->  循环累积 chromeChanged / plotChanged
- WM_SETCURSOR    if/else if 链      ->  从表尾向前找首个命中者,取其 QueryCursor

顺带兑现历史缺口:UiLayout::UiStackRects 自引入以来第一次有了生产调用点。

有意保留显式的三条链(WM_LBUTTONDOWN / WM_LBUTTONUP / WM_MOUSEMOVE):它们各自带
组件专属后置动作(提交重命名、构建派生信号、面板联动滚动、更新拖拽光标、
plotOnlyMove 决定局部失效还是整帧失效)。塞进表里只能靠 lambda 字段,
可读性反而下降 —— 属于用户明确禁止的"过度抽象",因此不做。

## 3. 大文件按职责拆分(纯整函数搬移,逐像素 0 差异)

SignalPanelInteraction.cpp 2329 -> 831 行,拆出:
  SignalPanelEdit.cpp   616  内联编辑(筛选框、组名/信号名、数值、颜色与透明度)
  SignalPanelScroll.cpp 205  动画与滚动(折起、联动滚动、滚动到目标信号)
  SignalPanelMenu.cpp   527  弹出菜单(系数/偏移、镜像、右键菜单与命令回执)

Shared/PopupMenu.cpp 1901 -> 706 行,拆出:
  PopupMenuLayout.cpp 753  尺寸测量、项矩形排版、滚动可见性
  PopupMenuPaint.cpp  397  背景/阴影/项绘制
  Inc/PopupMenuInternal.hpp 103   pmdetail 常量 + inline 辅助(跨 TU 安全)

拆分判据是"职责"而非行数;共享常量一律收敛到 *Internal.hpp。

## 4. 代码审查(修 5 处)

- SignalPanelInteraction.cpp 头注释仍写"4 片",实际 6 片 -> 改为完整清单
- PopupMenuLayout.cpp 机械搬移时把构造函数一起搬走 -> 重复定义,只保留在交互分片
- PopupMenu.cpp 的 #include "PopupMenuInternal.hpp" 落进 namespace jdi 内 -> 移到文件头
- PopupMenuPaint.cpp 缺 UiControls.hpp(UiDrawEditFieldContent 未声明)-> 补 include
- 全仓复查历史删除符号残留引用(m_paintPlot / ClampCursorsAfterPan /
  PaintFreeMarkerTooltip / CaptureDecorCache / FitSinglePointSignal / CsvTable /
  DataFile / .preflight / structcheck)-> 零命中

## 5. Tools/ 剥离

git rm -r --cached Tools + .gitignore 新增 Tools/(本地文件保留,可随时复跑)。
ui_refactor 分支为全本地分支,另做 filter-branch 清除历史中的该目录。

## 6. 验证

- make release / make debug:零 error 零 warning
- rendercheck:A-H 八个场景哈希与改造前逐一相同
  (A 2166933039626F66 B C444ECFEB18FD73D C 7984446D8E7A4418 D BF855879CA12F289
   E 1C663E4118616016 F 0535292D8E07AE18 G 7A58172C46577729 H 23F390DD76141113)
  FUNC 7/7 PASS
- uicheck:69 passed, 0 failed
- TargetCheck.exe 891,392 B(< 900 KB 阈值)

## 7. 文档

Documents/UI_Refactor_Plan.md / UI_Refactor_Progress.md / UI_Refactor_Report.md
新增第五阶段章节;Documents/agent_mem.md 同步变更日志与「第七轮」硬约束。
2026-10-28 14:35:32 +08:00
DeepSeek V4.1 Flash c3b79a6d2c UI 重构 P3(2):TabBar 迁移到控件树(逐像素 0 差异 + 4 条交互断言)
TabBar 的标签 / 「+」/ 覆盖层拆成控件,手势语义原样保留(自行判双击、3px 拖拽阈值、
关闭 ✕ 的「移开取消」、拖拽排序的 drop 位置),公开 API 未变:

- 新增(TabBar.cpp 内实现的内部控件)
  * UiTabItemWidget:单格标签的绘制(底色/描边/激活指示条/文本或重命名输入框/关闭 ✕),
    关闭按钮热区由基类的悬停点自行判定。
  * UiAddWidget:「+」按钮绘制与悬停配色。
  * UiOverlayWidget:命中透明的覆盖层,最后绘制「拖拽插入指示条 + 底部分隔线」
    (必须晚于标签与「+」,否则会被它们压住)。
  * UiTabRootWidget:只铺标签栏底色。
- TabBar 移除 m_hoverIndex / m_hoverClose / m_hoverAdd 三个手工下标,
  改为 dispatch MOUSE_MOVE 后由 root->TakeDirty() 判断是否需要重绘。
- 绘制顺序保持:底色 → 各标签 → 「+」→ 拖拽指示条 → 底部分隔线(与旧实现逐像素一致)。
- UiWidget 新增 HoverPoint(x,y):派生控件据此自行判断「悬停在自身内部哪个热区」。

验证:
- 像素:8 场景哈希与迁移前逐一相同(A 2166933…、H 23F390D…)。
- 交互断言(全部 PASS):
  * tabbar-hover-marks-dirty (1,0)         —— 进入标签只标记一次脏区;
  * tabbar-add-creates-tab (1 -> 2)        —— 「+」新建标签;
  * tabbar-click-selects-tab (active=0)    —— 点击标签切换激活页;
  * tabbar-close-removes-tab (2 -> 1)      —— 点击 ✕ 关闭标签。
- Tools/uicheck 69 passed / 0 failed。
- make release / make debug 零警告;Target.exe 886,784 B(< 900KB)。
2026-10-28 12:41:57 +08:00
DeepSeek V4.1 Flash 350538c634 命名审查与垃圾代码清理(纯重命名 / 死代码删除,无行为变更)
前提:加载完成后全部采样都已驻留内存,程序此后与源文件再无联系。
据此修正了一批会让人误以为"仍在读写文件"的命名:

- SampleFileAt → SampleSignalAt(取的是内存信号在给定时刻的采样值)。
- DataSet::AddPreparedFile → AddDataFile。
- PlotView::InvalidatePolylineCache → InvalidateDerivedCaches
  (它同时清空折线、边沿统计、中心平均值曲线、静态场景缓存与悬停引用)。
- PlotView::CursorColor(int which) → CursorColor()(两条光标同色,参数从未使用)。
- UiEllipsize(g,…) → UiEllipsize(…):纯测量不绘制,删掉被忽略的绘图上下文参数;
  同时删除与 UiMeasureTextWidth 完全重复的 UiTextWidth,调用点统一到 UiMeasureTextWidth。

清理残留参数((void)xxx;):
- TabConfig 的可见性/网格族(SetVisible / ToggleVisible / SetFileVisible / SetAllVisible /
  SoloSignal / SetMode / SyncLanes / FileHasVisible / FileAllVisible)去掉从未使用的
  const DataSet& 参数(仅 SyncToDataSet 真正需要数据层,保留)。
- PlotView::OnLButtonUp(int y)、OnMouseWheel(int delta, bool shift);PainterSignalHalo 去掉未用的 ref。
- BuildDataFile 中未使用的局部 rowCount。

删除无人调用的死函数:
  DataSet::Clear / AddFromTable;SignalColorCount;WideToUtf8 / StartsWith;
  PlotView::ViewMinTime / ViewMaxTime / EdgePanelRect / EdgePanelCloseRect /
  EdgeCenterPanelRect / ScaleLaneRange / BeginLaneValueEdit / LaneContentHeight;
  SignalPanel::NeedsScrollBar / IsFilterEditing / FilterText / ContentHeight;
  PopupMenu::EditingItemId;TabBar::IsDragging;TextEdit::PlaceCaret。

删除只写不读的死字段:CsvTable::sourceRowCount(与 rowCount 等义)与 skippedRowCount。

以 VERY_BIG_DATA.CSV 回归验证:加载与勾选、筛选框(含"无匹配的筛选信号")、
样式区线宽文案省略、选区放大(横向与纵向)、双光标读数与 Δt、右键菜单(Y 轴数值与
实验性功能子菜单)、合并/拆分网格、标签与信号重命名省略号,行为均与改动前一致;
make clean && make all 无任何警告。
2026-10-09 21:52:46 +08:00
DeepSeek V4.1 Flash 12b3531485 输入框鼠标编辑、图例省略、信号跨视图共享选择与网格菜单布局修复
- 输入框:支持鼠标左键点击定位插入符、拖拽框选区域,并且不再限制字符长度
- 输入框:长文本使用 ETO_CLIPPED 裁剪绘制,避免字符溢出到编辑区域之外
- 图例:信号名称超过 200px 时省略号截断,不再挤压标尺区域
- 选中状态从 TabConfig 迁移到 DataSet,切换视图后信号选中状态保持一致
- 网格右键菜单:Y 轴最大值/最小值直接显示当前值,点击后原位置弹出内联编辑框
- 标尺数值改为绘制在图例下层,不再因避让图例而隐藏
- 移除已无用的 LegendBandRect,新增共享的编辑框布局与命中检测工具
- .gitignore 忽略 TestData 测试数据目录
2026-09-30 17:10:23 +08:00
DeepSeek V4.1 Flash e3744f3eff fix: 统一输入框编辑行为、外观跨视图同步并优化标尺与网格可读性
输入框(TextEdit,全组件共用):
- 修复退格一次删除两个字符:WM_CHAR 与 WM_KEYDOWN 重复处理了退格/删除,
  现统一由 OnEditKey 处理,WM_CHAR 忽略全部控制字符。
- 进入编辑(整段选中)后按右键:取消全选并把插入符移到文本最右侧。
- 存在选区时松开 Shift 再按左右方向键,插入符收拢到选区的左/右端。
- 修复整段选中后按 Home/End 不收拢选区的问题。

标签页:
- 按加粗字体测量宽度,避免激活页长名称被提前省略(预览过短)。
- 重命名过程中按正在输入的文本测量并加宽上限(320px),标签随输入增长,
  解决长内容绘制错误;输入溢出时整体等比收缩,保证「+」始终可见。

信号显示外观跨标签页同步:
- 外观(颜色/自定义色记忆/透明度/线宽/样式)从 TabConfig 上移到 DataSet
  的 SignalData,成为信号的全局属性;可见性仍按标签页独立保存。
- 因此任一视图中修改某信号的颜色/线宽/透明度/样式,其它视图自动一致。

工具栏:
- 移除「全选信号」按钮;「清空勾选」改为「清空网格」,
  「拆分信号网格」改为「拆分网格」。

自适应:
- 无可见信号(未勾选/全部取消/清空网格)时复位 ViewInitialized,
  下次勾选第一个信号会重新自适应,而不是停留在旧视图范围。

绘图区标尺与网格:
- 幅值刻度密度按网格高度自适应(3-8 档),各网格刻度数量一致;
  网格较矮时同步收紧标签行距,提高可读密度。
- 网格线与五等分估读线加深,与网格边界(蓝灰)保持明显区分。
- 时间轴列名补上单位,显示为 time (s)。
2026-09-30 15:16:53 +08:00
DeepSeek V4.1 Flash 53768b8b25 fix: 统一输入框编辑能力、修复拖拽合并网格并支持光标吸附数据点
统一文本输入(新增 Utilities/Inc/TextEdit.hpp + TextEdit.cpp)
- 一个共享的编辑模型,提供光标/选区/过滤/限长,接入全部输入框:
  样式区数值与信号名/分组名、波形图例改名、视图标签页改名、
  拾色器 R/G/B 与色号、光标时间与网格上下限。
- 新增能力:Ctrl+A/C/X/V(含剪贴板清洗)、Shift+方向键与 Home/End 选区、
  退格与删除清除选区、代理对安全的光标步进。
- 渲染改为选区高亮 + 插入符,文本超出框宽时水平滚动并让插入符始终可见;
  初始态为整段选中,首字符键入即整体替换(保持"登录框"式交互)。
- 新增 Utf8Util::PasteTextFromClipboard(换行/制表符折叠为空格)。

修复网格合并(Utilities/Src/DataModel.cpp)
- 拖拽信号到其它网格时先固定目标网格的锚信号,再移除源信号并按引用找回目标,
  避免空网格回收导致下标错位,从"与原网格交换位置"改为真正合并。
- 自定义划分下未分配网格的可见信号改为各自独占一个新网格,
  修复"拖拽合并后再勾选新信号被并入该合并网格"的问题。

绘图区(Components/PlotView)
- 网格右键菜单新增"时间轴自适应(匹配本网格)",把时间轴对齐到该网格信号范围。
- 时间轴放大到采样点在屏幕上可分辨(>=6 逻辑像素)时,拖动光标吸附到最近数据点;
  缩小到无法分辨时保持自由移动,避免光标"跳格"。

验证:真实输入脚本逐项核对——拖拽合并后单网格含两条曲线、拖拽合并再勾选新信号得 G2 独立网格、
Shift 选区 + Delete 清除、Ctrl+A/C/V 复制粘贴、46 字符文本水平滚动、拾色器色号 1E90FF、
标签页改名 A1、光标时间框复制粘贴、全视图拖动得 0.522213(不吸附)而放大后得 0.28125(= 225 × 0.00125,已吸附)。
2026-09-30 13:40:58 +08:00
DeepSeek V4.1 Flash 4e295a2a7d fix: 修复图例改名不同步、细化网格标尺并支持标签页重排与曲线实色跟随拖拽
- 图例跟随重命名:绘图区图例文本改用信号当前显示名(Title),
  左侧列表改名后网格图例立即同步。
- 标签页重排与改名:新增拖拽排序(按住标签左右拖动并显示插入指示条,
  重排后保持原激活页)与双击内联改名,新增 MoveTab 正确处理激活下标。
  修复改名后未重新布局导致标签页整条消失的问题(SetTabs 会重置标签矩形)。
  双击判定改为自行按双击时间识别,不再依赖系统 WM_LBUTTONDBLCLK。
- 信号拖拽:虚影改用原信号颜色与线宽(不再画成虚线),保持原有线段/散点样式;
  横向与纵向均自由跟随鼠标位移;拖拽首帧位移为零,虚影始终贴合在鼠标下方。
- 网格与标尺细化:时间轴与幅值轴期望刻度数提高,并在相邻主刻度之间
  补绘 5 等分细线,便于目视估读。
- 图例定位与交互:图例移到各网格左上角,与标尺数字冲突时优先显示图例
  (标尺数值主动避让);图例条目支持单击选中信号(与左侧列表联动、
  样式区随之切换目标)与双击重命名信号。
- 网格划分:合并网格后新勾选的信号不再被并入那个唯一的合并网格,
  而是自动切换为自定义划分并独占一个新网格。
- 工具栏:「每信号一格」改名为「拆分信号网格」。
- 清理:移除已无引用的 UiDrawArrowIcon 与 m_styleContentRect。
2026-09-30 11:02:09 +08:00
DeepSeek V4.1 Flash 9c6c374cce feat: 支持多视图标签页、按文件分组的多文件信号管理与网格随勾选自动收敛
数据层与视图配置解耦:新增 DataSet(多文件只读数据层)与 TabConfig(每标签页独立配置),
以 SignalRef{fileIndex,signalIndex} 标识全局信号,替换原有单文件 SignalSet。

- 标签页:新增 TabBar 组件,支持新建/切换/关闭标签页;每页独立保存信号勾选、
  颜色/线宽/显示方式、网格划分、视图范围与时间光标,互不影响。
- 网格收敛:可见性变更统一经 TabConfig 的 Set*Visible 接口并同步重算网格,
  取消勾选后对应的空网格立即回收(修复此前留下空轨道的问题)。
- 多文件分组:左侧面板改为「文件分组 + 信号」树形列表,分组可折叠、可整组勾选,
  分组复选框支持全选/部分/未选三态;新加载文件的信号默认未勾选,由用户自行选择。
- 来源标识:信号行、样式编辑区标题、波形图例与光标读数在跨文件时标注来源文件名。
- 交互补充:面板滚动条支持点击跳转与拖拽,切换标签页自动滚动到已选信号。
- 工具栏精简为「打开 CSV/适应视图/放大/缩小/合并网格/每信号一格/全选信号/清空勾选」,
  按钮宽度改为按实际文本像素测量;打开对话框支持多选文件,拖放支持批量载入。
- 新增 TestData/[2].CSV 作为跨文件对比测试数据。
2026-09-29 15:48:04 +08:00