fix(ui): 采样统计显示一致化、GX 拖拽在布局边界下可用、信号「移动到…」与拖拽排序
一、采样间隔/频率显示不一致(用户报告 1ms vs 1000us、100us 0ns) * FormatDurationCompound 改为「先量化到整数皮秒、再整数分解」, 彻底消除同一数值因浮点噪声落到不同单位/留下零尾数的问题; 秒级分支与亚秒分支共用 totalPs 方案,次级进位/单位升级集中在一处。 * FormatFrequency 的单位选择改为按「舍入后的显示值」判定(999.9999999Hz 与 1000Hz 均为 1.000kHz),并做向下校正避免 0.001kHz。 二、选区截取面板 * 提示文本改为用户指定原文「启用双光标模式以进行选区截取」。 * EdgePanelWidth 计入该提示的实际文本宽度;绘制改用 UiTextClipped 不再省略。 * viewHeight 补回漏减的 bottomPad,修掉条目区与按钮之间的空白。 三、未展开的信号条目栏悬停不再变成手型光标。 四、开启「显示布局边界」后 GX 标签可正常拖拽排序 * 抽出 PaintLaneReorderOverlay(吸附槽位 + 被拖轮廓),贴图与整幅重绘共用。 * 整幅重绘在 LANE_REORDER 时按 LaneDragOffset 逐格平移重画(被拖网格最后画), 修掉调试模式下不跟随鼠标、无吸附提示、只能盲拖的问题。 五、信号右键新增「移动到…」二级菜单(位于「仅显示该信号」之上) * 条目为「分组 N(组名)」,当前组打勾;无标题行;底部「新建分组」。 * MenuItem::submenuMaxRows + PopupMenu 独立子菜单滚动(滚动条、滚轮、视口命中过滤), IsScrollable 计入子菜单可滚状态。 * 数据层 DataSet::MoveSignalToGroup/MoveSignal 让时间轴随信号搬运 (独占则移动、共享则复制);TabConfig::RemapAfterSignalMove 改写引用而非先删后插。 * Application::MoveSignalCore 统一入口,保证派生计算暂停、引用修正、选中迁移与同步一致。 六、信号列表支持按住拖拽排序(组内重排 / 跨组移动) * 4px 阈值进入拖拽;条目脱离原位跟随鼠标(阴影 + 强调色描边 + 插入指示线)。 * 其它信号行/分组行按插入位置做避让位移动画(RowRect 统一叠加插值位移, 宿主 kScrollTimerId 驱动 AdvanceSignalDragAnimation)。 * 数据层 DataSet::MoveSignal 支持任意位置;TabConfig::RemapAfterSignalReorder 线性重映射。 * 拖拽中禁用组名悬浮;双击改名与 InvalidateRows 均正确终止拖拽态。 验证:删 .o 全量重编译零 error / 零 warning;产物 873,984 B(< 1MB 预警阈值)。 本轮未使用鼠标/键盘/屏幕,未运行任何 GUI 探针,仅编译验证 + 源码级核对。
This commit is contained in:
1 parent
21a9865365
commit
97b361dedd
23 files changed
+1581
-74
No files matched your search
@@ -84,6 +84,140 @@ namespace jdi
|
||||
m_lanes.end());
|
||||
}
|
||||
|
||||
void TabConfig::RemapAfterSignalMove(const SignalRef& from, const SignalRef& to, bool visible)
|
||||
{
|
||||
if (from.groupIndex < 0 || to.groupIndex < 0 || from.groupIndex == to.groupIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 1) 可见性表:目标分组在目标位置插入一列(沿用源分组里的可见性),源分组删除一列。
|
||||
const std::size_t toSlot = static_cast<std::size_t>(to.groupIndex);
|
||||
if (toSlot < m_visible.size() &&
|
||||
static_cast<std::size_t>(to.signalIndex) <= m_visible[toSlot].size())
|
||||
{
|
||||
m_visible[toSlot].insert(m_visible[toSlot].begin() + to.signalIndex, visible);
|
||||
}
|
||||
const std::size_t fromSlot = static_cast<std::size_t>(from.groupIndex);
|
||||
if (fromSlot < m_visible.size() &&
|
||||
static_cast<std::size_t>(from.signalIndex) < m_visible[fromSlot].size())
|
||||
{
|
||||
m_visible[fromSlot].erase(m_visible[fromSlot].begin() + from.signalIndex);
|
||||
}
|
||||
|
||||
// 2) 全部信号引用:先改写被移动的那一条,再做 ±1 位移(顺序不可调换,见头文件说明)。
|
||||
const auto adjust = [&from, &to](SignalRef& ref)
|
||||
{
|
||||
if (ref.groupIndex == from.groupIndex && ref.signalIndex == from.signalIndex)
|
||||
{
|
||||
ref = to;
|
||||
return;
|
||||
}
|
||||
if (ref.groupIndex == to.groupIndex && ref.signalIndex >= to.signalIndex)
|
||||
{
|
||||
++ref.signalIndex;
|
||||
}
|
||||
else if (ref.groupIndex == from.groupIndex && ref.signalIndex > from.signalIndex)
|
||||
{
|
||||
--ref.signalIndex;
|
||||
}
|
||||
};
|
||||
// 条目集合都是「带 `ref` 成员的结构体」,用泛型 lambda 统一处理(与
|
||||
// `RemapAfterSignalRemoval` 的 `purgeRefs` 同一手法)。
|
||||
const auto adjustItems = [&adjust](auto& items)
|
||||
{
|
||||
for (auto& item : items)
|
||||
{
|
||||
adjust(item.ref);
|
||||
}
|
||||
};
|
||||
|
||||
for (Lane& lane : m_lanes)
|
||||
{
|
||||
for (SignalRef& ref : lane.signals)
|
||||
{
|
||||
adjust(ref);
|
||||
}
|
||||
adjustItems(lane.meanFilterItems);
|
||||
adjustItems(lane.edgeCenterItems);
|
||||
adjustItems(lane.weakPeakItems);
|
||||
adjustItems(lane.resampleItems);
|
||||
adjustItems(lane.clipItems);
|
||||
adjustItems(lane.freeMarkerItems);
|
||||
}
|
||||
}
|
||||
|
||||
void TabConfig::RemapAfterSignalReorder(int groupIndex, int from, int to)
|
||||
{
|
||||
if (groupIndex < 0 || from < 0 || to < 0 || from == to)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 线性重排映射:被移动的那条改为 `to`,被它跨过的条目各让一位。
|
||||
const auto mapIndex = [from, to](int index)
|
||||
{
|
||||
if (index == from)
|
||||
{
|
||||
return to;
|
||||
}
|
||||
if (from < to)
|
||||
{
|
||||
if (index > from && index <= to)
|
||||
{
|
||||
return index - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index >= to && index < from)
|
||||
{
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
// 可见性表整体重排(与信号顺序严格一致)。
|
||||
const std::size_t slot = static_cast<std::size_t>(groupIndex);
|
||||
if (slot < m_visible.size() && static_cast<std::size_t>(from) < m_visible[slot].size() &&
|
||||
static_cast<std::size_t>(to) < m_visible[slot].size())
|
||||
{
|
||||
std::vector<bool>& row = m_visible[slot];
|
||||
const bool moved = row[static_cast<std::size_t>(from)];
|
||||
row.erase(row.begin() + from);
|
||||
row.insert(row.begin() + to, moved);
|
||||
}
|
||||
|
||||
const auto adjust = [groupIndex, &mapIndex](SignalRef& ref)
|
||||
{
|
||||
if (ref.groupIndex == groupIndex)
|
||||
{
|
||||
ref.signalIndex = mapIndex(ref.signalIndex);
|
||||
}
|
||||
};
|
||||
const auto adjustItems = [&adjust](auto& items)
|
||||
{
|
||||
for (auto& item : items)
|
||||
{
|
||||
adjust(item.ref);
|
||||
}
|
||||
};
|
||||
|
||||
for (Lane& lane : m_lanes)
|
||||
{
|
||||
for (SignalRef& ref : lane.signals)
|
||||
{
|
||||
adjust(ref);
|
||||
}
|
||||
adjustItems(lane.meanFilterItems);
|
||||
adjustItems(lane.edgeCenterItems);
|
||||
adjustItems(lane.weakPeakItems);
|
||||
adjustItems(lane.resampleItems);
|
||||
adjustItems(lane.clipItems);
|
||||
adjustItems(lane.freeMarkerItems);
|
||||
}
|
||||
}
|
||||
|
||||
void TabConfig::RemapAfterSignalRemoval(int groupIndex, int signalIndex)
|
||||
{
|
||||
if (groupIndex < 0 || signalIndex < 0)
|
||||
|
||||
Reference in new issue
Block a user