标题栏内存占用 / 样式区增益与取绝对值·中心反转 / 拾色器排版 / 图例去托底与命中优先级
标题栏: - 应用名称后追加「 | RAM : <值>」:竖线两侧各留两个空格(与分组标题「组X | 组名」的间距风格一致)。 - 数值按 KB/MB/GB 自适应、保留两位小数(复用 FormatByteSize);用 K32GetProcessMemoryInfo 取本进程 工作集(与任务管理器「内存」一致,无需新增链接依赖);1 秒定时器刷新,数值未变时不调用 SetWindowTextW。 信号样式自定义栏: - 线宽与透明度交换上下位置(线宽在上、透明度在下)。 - 新增「系数增益」:输入框(默认 1,只接受数值,范围 ±10000),点击进入编辑、回车/点击别处提交。 - 新增「取绝对值」「中心反转」两个同行的切换按钮。 - 三者作用顺序固定为「系数增益 → 取绝对值 → 中心反转」(中心反转最后结算),实现为 ApplyDrawTransform(style, value),在 BuildPolyline(逐点描边与像素列包络两条路径,含粗粒度分组层)、 派生预览曲线与光标取值读数统一调用;样式属于信号本身(跨视图共享)并进入 SceneContentHash / SceneStyleEntry / 折线缓存键,改动会立即重绘。 - 非保序变换(取绝对值 / 反转 / 负增益)会回退到逐采样点变换、不再使用粗粒度包络层,保证包络精确。 自定义颜色面板: - 把底部的「色号 + 预览色块」移到 RGB 滑块上方;右侧一列(预览行 + 三行滑块)与左侧「色域方块 + 色相条」高度严格相等,两列上下边对齐,面板不再为它单独保留一行。 网格内: - 去掉图例与 GX 编号下方的整块不透明托底(它会把网格线整片擦掉,看起来像一个"可点击控件", 而其右侧空白实际并不响应点击)。 - 图例与 GX 徽标改为优先命中:先于时间光标线 / 光标读数 / 边沿计数横向光标 / 选区放大判定, 因此选区放大模式下也能正常拖拽 GX 标签与点选图例(浮动面板仍然优先,与绘制层序一致)。 验证:离屏 harness(.preflight/uitest,SW_HIDE、不动鼠标键盘屏幕)新增 [AK] 标题栏内存读数(格式与竖线间隙、单位阈值、两位小数、随 256MB 分配增长、定时器刷新); [AL] 样式区行序与几何、增益输入提交、两个切换按钮的真实点击、ApplyDrawTransform 顺序,以及 BuildPolyline 顶点范围与变换后数据范围逐项对齐(默认 / 增益 2 / 取绝对值 / 中心反转 / 负增益+取绝对值+中心反转叠加); [AM] 拾色器两列严格等高(140.00 == 140.00); [AN] 选区放大模式下 GX 拖拽重排 + 图例点选 + 网格线穿过图例带(托底已移除)。 另修正旧用例 [AI](远端已删除"光标跟随视界",改为断言光标时间位置不变、允许滑出视窗)。 合计 530/530 通过;make clean && make all 零警告。
This commit is contained in:
1 parent
20b90848fe
commit
c3343907ec
8 files changed
+420
-105
No files matched your search
@@ -4654,22 +4654,17 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
|
||||
// 靠近时间光标线时优先进入拖动,避免误触发平移或缩放选区。
|
||||
{
|
||||
int cursorWhich = 0;
|
||||
if (IsOnCursorLine(x, cursorWhich) && UiPointInRect(m_area, fx, fy))
|
||||
{
|
||||
m_dragMode = PlotDragMode::CURSOR_MOVE;
|
||||
m_hoverCursor = cursorWhich;
|
||||
// 点中的光标成为“活动光标”:它触发的内容将画在上层。
|
||||
m_activeCursor = cursorWhich;
|
||||
m_cursorHover = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 图例与 GX 编号徽标叠在波形之上、且直接位于网格顶部——用户明确要求它们**优先响应**:
|
||||
// 必须先于「时间光标线 / 光标读数 / 边沿计数横向光标 / 选区放大」判定,否则:
|
||||
// * 光标线恰好经过徽标/图例时,按下会变成拖光标;
|
||||
// * 选区放大模式下会变成“开始框选”,用户看到的就是“GX 拖不动、图例点不中”。
|
||||
// 层序上仍然让位于浮动面板(面板绘制在徽标与图例之上),因此先挡一次面板命中。
|
||||
int guardLane = -1;
|
||||
int guardKind = 0;
|
||||
const bool guardPanel = TopPanelAtPoint(fx, fy, guardLane, guardKind);
|
||||
|
||||
// 网格次序拖拽:按在图例的 GX 编号徽标上即可整块拎起该网格上下移动。
|
||||
// 位置在浮动面板之后(面板压在波形之上,应优先命中),但在信号拖拽之前。
|
||||
if (!guardPanel)
|
||||
{
|
||||
const int badgeLane = LaneBadgeAtPoint(x, y);
|
||||
if (badgeLane >= 0)
|
||||
@@ -4686,6 +4681,36 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
|
||||
// 点击图例条目等价于在左侧列表中选中该信号(样式区随之切换目标)。
|
||||
// **必须在选区放大判定之前**:选区放大模式只改变拖拽语义,
|
||||
// 不应影响图例的悬停与点选(否则用户在选区放大模式下无法切换信号)。
|
||||
if (!guardPanel)
|
||||
{
|
||||
const SignalRef legendHit = LegendAtPoint(x, y);
|
||||
if (legendHit.IsValid())
|
||||
{
|
||||
SelectSignal(legendHit);
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 靠近时间光标线时进入拖动。
|
||||
// (放在徽标/图例之后:两者重叠时徽标与图例优先,与用户要求一致。)
|
||||
if (!guardPanel)
|
||||
{
|
||||
int cursorWhich = 0;
|
||||
if (IsOnCursorLine(x, cursorWhich) && UiPointInRect(m_area, fx, fy))
|
||||
{
|
||||
m_dragMode = PlotDragMode::CURSOR_MOVE;
|
||||
m_hoverCursor = cursorWhich;
|
||||
// 点中的光标成为“活动光标”:它触发的内容将画在上层。
|
||||
m_activeCursor = cursorWhich;
|
||||
m_cursorHover = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 实验性功能浮动面板(面板压在波形之上,必须先于信号拖拽判定):
|
||||
// * ✕ → 关闭该网格对应的功能;
|
||||
// * 逐条目参数输入框 → 进入内联编辑;
|
||||
@@ -4976,19 +5001,6 @@ namespace jdi
|
||||
return false;
|
||||
}
|
||||
|
||||
// 点击图例条目等价于在左侧列表中选中该信号(样式区随之切换目标)。
|
||||
// **必须在选区放大判定之前**:选区放大模式只改变拖拽语义,
|
||||
// 不应影响图例的悬停与点选(否则用户在选区放大模式下无法切换信号)。
|
||||
{
|
||||
const SignalRef legendHit = LegendAtPoint(x, y);
|
||||
if (legendHit.IsValid())
|
||||
{
|
||||
SelectSignal(legendHit);
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const PlotZoomMode mode = Tab().ZoomMode();
|
||||
if (mode == PlotZoomMode::SELECT)
|
||||
{
|
||||
@@ -5330,6 +5342,10 @@ namespace jdi
|
||||
const std::size_t sampleCount = std::min(resolved.file->time.size(), resolved.data->values.size());
|
||||
const double viewMin = Tab().ViewMin();
|
||||
const double viewMax = Tab().ViewMax();
|
||||
// 绘制前的数值变换(增益/绝对值/中心反转)会改变顶点,必须进缓存键。
|
||||
const float gain = (resolved.style != nullptr) ? resolved.style->gain : 1.0f;
|
||||
const bool absolute = (resolved.style != nullptr) && resolved.style->absolute;
|
||||
const bool inverted = (resolved.style != nullptr) && resolved.style->inverted;
|
||||
|
||||
for (const PolylineCacheEntry& entry : m_polylineCache)
|
||||
{
|
||||
@@ -5337,7 +5353,8 @@ namespace jdi
|
||||
entry.viewMin == viewMin && entry.viewMax == viewMax && entry.plotX == m_plotRect.X &&
|
||||
entry.plotWidth == m_plotRect.Width && entry.laneY == lane.rect.Y &&
|
||||
entry.laneHeight == lane.rect.Height && entry.laneYMin == lane.yMin &&
|
||||
entry.laneYMax == lane.yMax)
|
||||
entry.laneYMax == lane.yMax && entry.gain == gain && entry.absolute == absolute &&
|
||||
entry.inverted == inverted)
|
||||
{
|
||||
return &entry;
|
||||
}
|
||||
@@ -5362,6 +5379,9 @@ namespace jdi
|
||||
entry.laneHeight = lane.rect.Height;
|
||||
entry.laneYMin = lane.yMin;
|
||||
entry.laneYMax = lane.yMax;
|
||||
entry.gain = gain;
|
||||
entry.absolute = absolute;
|
||||
entry.inverted = inverted;
|
||||
|
||||
BuildPolyline(resolved, lane, entry.points, entry.breaks, entry.band, entry.bandBreaks,
|
||||
entry.bandMode, entry.bandSpiky);
|
||||
@@ -5511,8 +5531,13 @@ namespace jdi
|
||||
const std::vector<double>& coarseMin = resolved.data->coarseMin;
|
||||
const std::vector<double>& coarseMax = resolved.data->coarseMax;
|
||||
const std::size_t coarseStride = resolved.data->coarseStride;
|
||||
// ⚠️ 粗粒度分组只存了逐组的 min/max:取绝对值 / 中心反转会把两者互换,
|
||||
// 此时分组层无法再给出正确的包络,必须回退到逐采样点变换(见 `DrawTransformOrderPreserving`)。
|
||||
const bool hasCoarse =
|
||||
(coarseStride > 0) && !coarseMin.empty() && (coarseMin.size() == coarseMax.size());
|
||||
(coarseStride > 0) && !coarseMin.empty() && (coarseMin.size() == coarseMax.size()) &&
|
||||
DrawTransformOrderPreserving(*resolved.style);
|
||||
// 保序变换(正增益)下,分组极值直接乘增益即为变换后的极值(gain==1 时逐位不变)。
|
||||
const double coarseScale = static_cast<double>(resolved.style->gain);
|
||||
|
||||
bool inSegment = false;
|
||||
|
||||
@@ -5566,8 +5591,8 @@ namespace jdi
|
||||
std::min((i1 - 1) / coarseStride, coarseMin.size() - 1);
|
||||
for (std::size_t bucket = firstBucket; bucket <= lastBucket; ++bucket)
|
||||
{
|
||||
const double bucketLow = coarseMin[bucket];
|
||||
const double bucketHigh = coarseMax[bucket];
|
||||
const double bucketLow = coarseMin[bucket] * coarseScale;
|
||||
const double bucketHigh = coarseMax[bucket] * coarseScale;
|
||||
if (!std::isfinite(bucketLow))
|
||||
{
|
||||
// 该分组全部是无效值:包络到此断开,后续另起一段。
|
||||
@@ -5590,7 +5615,7 @@ namespace jdi
|
||||
{
|
||||
for (std::size_t index = i0; index < i1; ++index)
|
||||
{
|
||||
const double value = values[index];
|
||||
const double value = ApplyDrawTransform(*resolved.style, values[index]);
|
||||
if (!std::isfinite(value))
|
||||
{
|
||||
// 该列含无效值:包络绘制到本列结束,后续另起一段。
|
||||
@@ -5624,6 +5649,7 @@ namespace jdi
|
||||
double boundary = 0.0;
|
||||
if (PolylineValueAt(time, values, count, i0, t0, boundary))
|
||||
{
|
||||
boundary = ApplyDrawTransform(*resolved.style, boundary);
|
||||
if (boundary < low)
|
||||
{
|
||||
low = boundary;
|
||||
@@ -5637,6 +5663,7 @@ namespace jdi
|
||||
}
|
||||
if (PolylineValueAt(time, values, count, i1, t1, boundary))
|
||||
{
|
||||
boundary = ApplyDrawTransform(*resolved.style, boundary);
|
||||
if (boundary < low)
|
||||
{
|
||||
low = boundary;
|
||||
@@ -5722,7 +5749,7 @@ namespace jdi
|
||||
for (std::size_t index = begin; index < end; index += stride)
|
||||
{
|
||||
const double t = time[index];
|
||||
const double value = values[index];
|
||||
const double value = ApplyDrawTransform(*resolved.style, values[index]);
|
||||
if (!std::isfinite(t) || !std::isfinite(value))
|
||||
{
|
||||
inSegment = false;
|
||||
@@ -6386,6 +6413,10 @@ namespace jdi
|
||||
mixFloat(resolved.style->opacity);
|
||||
mixFloat(resolved.style->lineWidth);
|
||||
mix(static_cast<std::uint64_t>(resolved.style->mode));
|
||||
// 绘制前的数值变换(增益 / 取绝对值 / 中心反转)会改变波形形状。
|
||||
mixFloat(resolved.style->gain);
|
||||
mix(resolved.style->absolute ? 1ull : 0ull);
|
||||
mix(resolved.style->inverted ? 1ull : 0ull);
|
||||
}
|
||||
// 图例文案参与绘制,重命名必须让缓存失效。
|
||||
mixText(resolved.data->Title());
|
||||
@@ -6431,6 +6462,9 @@ namespace jdi
|
||||
entry.opacity = resolved.style->opacity;
|
||||
entry.lineWidth = resolved.style->lineWidth;
|
||||
entry.mode = static_cast<int>(resolved.style->mode);
|
||||
entry.gain = resolved.style->gain;
|
||||
entry.absolute = resolved.style->absolute;
|
||||
entry.inverted = resolved.style->inverted;
|
||||
}
|
||||
lane.push_back(entry);
|
||||
}
|
||||
@@ -8851,7 +8885,7 @@ namespace jdi
|
||||
int visibleCount = 0;
|
||||
int drawnCount = 0;
|
||||
|
||||
// 先测量图例总宽度,再用半透明底板掠开底下的网格线,提升可读性。
|
||||
// 先测量图例总宽度:只用来确定装饰缓存矩形(不再绘制任何托底/底板)。
|
||||
float legendWidth = 0.0f;
|
||||
for (const SignalRef& ref : laneSignals)
|
||||
{
|
||||
@@ -8877,23 +8911,24 @@ namespace jdi
|
||||
legendWidth += itemWidth;
|
||||
}
|
||||
{
|
||||
// 底板从编号徽标开始一直盖到最后一个图例条目(编号徽标一并被掠开底下的网格线)。
|
||||
// 底板范围(编号徽标 + 图例条目):这里**只记录装饰矩形**用于缓存贴回。
|
||||
const float plateRight = cursorX + legendWidth + (legendWidth > 0.0f ? 2.0f * m_scale : 0.0f);
|
||||
// 记录图例装饰矩形(含编号徽标):从徽标左缘到图例右缘。
|
||||
{
|
||||
const float left = std::min(badgeRect.X, legendBand.X - 4.0f * m_scale) - 2.0f * m_scale;
|
||||
const float right = std::max(badgeRect.X + badgeRect.Width, plateRight) + 2.0f * m_scale;
|
||||
m_decorRects.push_back(gd::RectF(left, top - 4.0f * m_scale, right - left,
|
||||
height + 8.0f * m_scale));
|
||||
}
|
||||
// 命中装饰缓存:整块图例(底板 + 徽标 + 色块 + 文字)改由装饰层贴回,跳过重绘。
|
||||
// ⚠️ **不再绘制不透明托底**(用户要求去掉):原来的底板会把徽标与图例下方的
|
||||
// 网格线整块擦掉,使这一条看起来像一个"可点击的控件",而它右侧的空白区域
|
||||
// 实际并不响应点击(选区放大模式下会变成开始框选)。现在图例与徽标直接叠在
|
||||
// 网格线之上,命中范围与看到的控件一一对应。
|
||||
//
|
||||
// 命中装饰缓存:整块图例(徽标 + 色块 + 文字)改由装饰层贴回,跳过重绘。
|
||||
if (m_useDecorCache)
|
||||
{
|
||||
return;
|
||||
}
|
||||
UiFillRoundRect(g, gd::RectF(legendBand.X - 4.0f * m_scale, top - 2.0f * m_scale,
|
||||
plateRight - legendBand.X + 4.0f * m_scale, height + 4.0f * m_scale),
|
||||
3.0f * m_scale, m_theme->cardBg | 0xFF000000u);
|
||||
}
|
||||
|
||||
// 编号徽标绘制在底板之上(与信号面板的 GX 徽标使用同一套配色与圆角)。
|
||||
@@ -9366,6 +9401,8 @@ namespace jdi
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 取值读数必须与画出来的波形一致:同样套用增益/取绝对值/中心反转。
|
||||
value = ApplyDrawTransform(*resolved.style, value);
|
||||
|
||||
const float rawY = ValueToY(value, lane);
|
||||
// 上下越界的数据点不再被丢弃:与左右越界一致,贴边绘制指示方向的三角。
|
||||
@@ -9388,6 +9425,7 @@ namespace jdi
|
||||
double otherValue = 0.0;
|
||||
if (SampleSignalAt(*resolved.file, *resolved.data, otherTime, interpolate, otherValue))
|
||||
{
|
||||
otherValue = ApplyDrawTransform(*resolved.style, otherValue);
|
||||
const double delta = value - otherValue;
|
||||
// 增量 = 右侧光标取值 − 左侧光标取值,包在圆括号里并按差值显式给出正负号。
|
||||
text += L" (";
|
||||
@@ -10318,7 +10356,7 @@ namespace jdi
|
||||
inSegment = false;
|
||||
continue;
|
||||
}
|
||||
float y = ValueToY(static_cast<double>(sampled), lane);
|
||||
float y = ValueToY(ApplyDrawTransform(*resolved.style, static_cast<double>(sampled)), lane);
|
||||
y = static_cast<float>(ClampDouble(y, limitTop, limitBottom));
|
||||
const float x = TimeToX(time[index]);
|
||||
if (!inSegment)
|
||||
|
||||
Reference in new issue
Block a user