数据层大重构:文件→分组 / 时间轴一等公民 / 加载器抽象 + CSV 多时间轴 + MF4 支持
一、数据模型解耦(详见 Documents/refactor_report_2026-10-15.md 与 agent_mem §9.27)
1. DataFile → SignalGroup:删除 filePath / fileName / time / timeColumnName / timeMonotonic /
minTime / maxTime —— 「分组」是应用内的逻辑容器,加载完成后与磁盘文件再无关系。
2. 新增 TimeAxis(name / time / minTime / maxTime / monotonic / finiteCount),一个分组可含 1..N 条;
SignalData 新增 axisIndex 指向所属时间轴,采样值与时间的对应关系自包含。
3. SignalData 删除冗余的 rawHeader(name 的副本)与 unit(由列名猜出、全仓无人使用);
名字只保留 name(源名称)+ displayName(用户重命名),Title() 统一展示。
4. SignalRef::fileIndex → groupIndex;DataSet:AddGroup/RemoveGroup/GroupAt/GroupCount/Groups()/
SetGroupDisplayName/RemapSelectionAfterGroupRemoval + 新增 Axis(ref);
TabConfig:SetGroupVisible/GroupHasVisible/GroupAllVisible/RemapAfterGroupRemoval +
RowKind::GROUP / CMD_GROUP_*。
5. 采样与统计 API 首参由 const DataFile& 改为 const TimeAxis&:
SampleSignalAt / SampleDisplayedSignalAt / FindNearestSample(Utilities)与
SampleSpacingPixels / VisibleIndexRange / EdgeIndexRange / MeanFilterValueAt / WeakPeakValueAt /
EdgeCountsFor / EdgeCenterCountsFor / EdgeScanFor(PlotView)。
6. 视图层:ResolvedSignal = { data, style, group, axis, visible };PolylineCacheEntry::file→group;
EdgeScanState::file→axis(统计身份 = 时间轴 + 信号);
ComputeVisibleTimeBounds 只统计可见信号真正用到的时间轴。
二、加载层抽象(格式与模型彻底解耦)
- 新增 Utilities/Inc/DataTable.hpp:DataTable(通用列优先表格)+ LoadProgress/Info +
TableBlock(一个时间轴块)+ LoadedSource(源文件全部块)。
- 新增 Utilities/Inc|Src/DataLoader.*:唯一入口 LoadSignalSource 按扩展名分发 +
IsSupportedSignalFile + SignalFileDialogFilter。
- 新增 Utilities/Inc|Src/CsvLoader.*:CSV 解析 + 多时间轴切分 + 各轴按自己的时间列裁剪。
- 新增 Utilities/Inc|Src/Mf4Loader.*:ASAM MDF v4 解析。
- 删除 Utilities/Inc/CsvTable.hpp 与 Utilities/Src/CsvTable.cpp。
- 唯一装配点 BuildSignalGroup(LoadedSource&&, paletteBase, group, error, progress):
时间轴去重合并 + 信号命名 + 调色板取色 + 统计与粗粒度包络;新增格式只需写一个加载器。
三、CSV 多时间轴(修正用户报告的缺陷)
- 只有名为 t/time/timestamp/simulation time/时间/时间轴 的列是时间轴(判据收紧,
不再用「包含 time」以免把 timeout 误判成时间轴)。
- 每个时间列开启一个新块,其后直到下一个时间列之前的列都属于该块;各块按自己的时间列裁剪
⇒ 支持各轴长度不同(Multi_Timeline.CSV 实测切出 3 条轴,长度 7/4/7);
**不再把其它 time 列当成信号显示**。
- 完全没有时间列时保留旧行为(第一列当时间轴,单轴)。
四、MF4 支持(新功能)
- 加载后新建一个分组、组名 = 文件名,文件内全部信号归入该分组。
- 每个通道组(CG)是一条时间轴,内容相同的时间轴自动合并(实测 27 个 CG → 3 条轴 / 124 条信号)。
- 实现 MDF v4 块解析(HD/DG/CG/CN/CC/DT)、1–64 位整数(任意位偏移、含符号扩展)与
32/64 位 IEEE 浮点解码、无效位(inval bit)处理、线性换算 phys = cc_val[0] + cc_val[1]*raw。
- TX 文本块同时兼容规范写法(U32 长度 + UTF-16/UTF-8)与 CANape 的「无长度前缀 NUL 结尾裸文本」。
- 未实现(跳过该组):压缩块 ##DZ/##HL/##SD、##DL 数据列表、字符串/MIME 通道。
五、顺带修复
- Application::Initialize:窗口类已注册(ERROR_CLASS_ALREADY_EXISTS)时复用而不是弹模态错误框
(旧实现让同一进程第二次构造 Application 永久阻塞);Shutdown 注销窗口类并销毁窗口。
- UI 文案统一到「分组」:选中该分组全部信号 / 取消该分组全部信号 / 关闭该分组(从工作区移除)/
请关闭部分分组或信号后重试。/ 尚未加载数据,请打开数据文件 / 打开数据文件以查看波形 /
(可同时加载多个文件,各自成为一个分组);对话框过滤器增加 *.mf4/*.mdf。
六、验证(全部离屏:BELOW_NORMAL 优先级 + SW_HIDE 窗口 + 只注入合成消息)
- 新增 .preflight/perf/datacheck_main.cpp:69/69 通过(单轴 CSV 回归、多时间轴 CSV、MF4 组名/信号数/
时间轴/主时间轴 ns→s/setSpeed 首样本与原始字节人工核对、DataSet 不变量、派生信号沿用源轴、
MF4 集成 124 信号并一格 60~68ms/帧)。
- 新增 .preflight/perf/edgecheck_main.cpp(ASCII-only,替代被编码破坏的 panelcheck):46/46 通过
(「调整」符号规则走真实菜单路径、边沿统计按帧推进/不饿死/逐位一致/无鼠标输入也完成、
面板两列等宽与信号名限宽、弱峰复位控件)。
- 既有 .preflight/perf/verify_main.cpp(已适配新 API):47/47,另有 2 条本机未同步的旧规则断言
(derived offsetTime kept / mouse in lane1 lower half),与本轮无关。
- make clean && make all 零警告。
This commit is contained in:
1 parent
fba40ee1a2
commit
6eedbe3c5d
18 files changed
+2545
-817
No files matched your search
@@ -28,9 +28,9 @@ namespace jdi
|
||||
CMD_SIGNAL_SOLO = 2, /**< 仅显示该信号。 */
|
||||
CMD_SIGNAL_SHOW_ALL = 3, /**< 显示全部信号。 */
|
||||
CMD_SIGNAL_HIDE_ALL = 4, /**< 隐藏全部信号。 */
|
||||
CMD_FILE_SHOW_ALL = 10, /**< 选中该文件全部信号。 */
|
||||
CMD_FILE_HIDE_ALL = 11, /**< 取消该文件全部信号。 */
|
||||
CMD_FILE_REMOVE = 12, /**< 从工作区移除该文件。 */
|
||||
CMD_GROUP_SHOW_ALL = 10, /**< 选中该文件全部信号。 */
|
||||
CMD_GROUP_HIDE_ALL = 11, /**< 取消该文件全部信号。 */
|
||||
CMD_GROUP_REMOVE = 12, /**< 从工作区移除该文件。 */
|
||||
CMD_COLOR_BASE = 100, /**< 预设颜色基准(配合色块下标)。 */
|
||||
CMD_MODE_BASE = 200, /**< 绘制样式基准(配合下标)。 */
|
||||
CMD_LANE_BASE = 300, /**< 网格归属基准(配合下标)。 */
|
||||
@@ -315,7 +315,7 @@ namespace jdi
|
||||
*/
|
||||
enum class RowKind : int
|
||||
{
|
||||
FILE = 0, /**< 文件分组标题行。 */
|
||||
GROUP = 0, /**< 文件分组标题行。 */
|
||||
SIGNAL = 1 /**< 信号行。 */
|
||||
};
|
||||
|
||||
@@ -359,7 +359,7 @@ namespace jdi
|
||||
struct ListRow
|
||||
{
|
||||
RowKind kind = RowKind::SIGNAL; /**< 行类型。 */
|
||||
int fileIndex = -1; /**< 所属文件下标。 */
|
||||
int groupIndex = -1; /**< 所属文件下标。 */
|
||||
int signalIndex = -1; /**< 信号下标(仅信号行有效)。 */
|
||||
float height = 0.0f; /**< 行高(像素)。 */
|
||||
};
|
||||
@@ -478,11 +478,11 @@ namespace jdi
|
||||
|
||||
/**
|
||||
* @brief 弹出文件分组右键菜单。
|
||||
* @param [IN] fileIndex 目标文件下标。
|
||||
* @param [IN] groupIndex 目标文件下标。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
* @param [IN] y 客户区纵坐标。
|
||||
*/
|
||||
void OpenFileMenu(int fileIndex, int x, int y);
|
||||
void OpenFileMenu(int groupIndex, int x, int y);
|
||||
|
||||
/**
|
||||
* @brief 弹出网格归属菜单(由信号行的网格徽标触发)。
|
||||
|
||||
@@ -448,17 +448,17 @@ namespace jdi
|
||||
const float signalHeight = kSignalRowHeight * m_scale;
|
||||
const bool filtering = !m_filterText.empty();
|
||||
|
||||
const std::vector<DataFile>& files = m_dataSet->Files();
|
||||
for (std::size_t fileIndex = 0; fileIndex < files.size(); ++fileIndex)
|
||||
const std::vector<SignalGroup>& files = m_dataSet->Groups();
|
||||
for (std::size_t groupIndex = 0; groupIndex < files.size(); ++groupIndex)
|
||||
{
|
||||
// 筛选时先挑出本组内匹配的信号;整组都没有匹配则**不显示该组**。
|
||||
std::vector<int> matched;
|
||||
if (filtering)
|
||||
{
|
||||
const std::size_t signalCount = files[fileIndex].signals.size();
|
||||
const std::size_t signalCount = files[groupIndex].signals.size();
|
||||
for (std::size_t signalIndex = 0; signalIndex < signalCount; ++signalIndex)
|
||||
{
|
||||
if (FilterMatchesSignal(files[fileIndex].signals[signalIndex]))
|
||||
if (FilterMatchesSignal(files[groupIndex].signals[signalIndex]))
|
||||
{
|
||||
matched.push_back(static_cast<int>(signalIndex));
|
||||
}
|
||||
@@ -470,13 +470,13 @@ namespace jdi
|
||||
}
|
||||
|
||||
ListRow fileRow;
|
||||
fileRow.kind = RowKind::FILE;
|
||||
fileRow.fileIndex = static_cast<int>(fileIndex);
|
||||
fileRow.kind = RowKind::GROUP;
|
||||
fileRow.groupIndex = static_cast<int>(groupIndex);
|
||||
fileRow.height = fileHeight;
|
||||
m_rows.push_back(fileRow);
|
||||
|
||||
// 筛选结果**默认展开**(忽略折叠状态),便于直接看到匹配项。
|
||||
if (!filtering && m_tab->IsCollapsed(static_cast<int>(fileIndex)))
|
||||
if (!filtering && m_tab->IsCollapsed(static_cast<int>(groupIndex)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -487,7 +487,7 @@ namespace jdi
|
||||
{
|
||||
ListRow signalRow;
|
||||
signalRow.kind = RowKind::SIGNAL;
|
||||
signalRow.fileIndex = static_cast<int>(fileIndex);
|
||||
signalRow.groupIndex = static_cast<int>(groupIndex);
|
||||
signalRow.signalIndex = signalIndex;
|
||||
signalRow.height = signalHeight;
|
||||
m_rows.push_back(signalRow);
|
||||
@@ -495,12 +495,12 @@ namespace jdi
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::size_t signalCount = files[fileIndex].signals.size();
|
||||
const std::size_t signalCount = files[groupIndex].signals.size();
|
||||
for (std::size_t signalIndex = 0; signalIndex < signalCount; ++signalIndex)
|
||||
{
|
||||
ListRow signalRow;
|
||||
signalRow.kind = RowKind::SIGNAL;
|
||||
signalRow.fileIndex = static_cast<int>(fileIndex);
|
||||
signalRow.groupIndex = static_cast<int>(groupIndex);
|
||||
signalRow.signalIndex = static_cast<int>(signalIndex);
|
||||
signalRow.height = signalHeight;
|
||||
m_rows.push_back(signalRow);
|
||||
@@ -639,7 +639,7 @@ namespace jdi
|
||||
{
|
||||
const int rowIndex = static_cast<int>(index);
|
||||
const gd::RectF rect = RowRect(rowIndex);
|
||||
const bool isFile = (row.kind == RowKind::FILE);
|
||||
const bool isFile = (row.kind == RowKind::GROUP);
|
||||
const float baseX = rect.X + kPadding * m_scale + (isFile ? 0.0f : kIndent * m_scale);
|
||||
|
||||
const gd::RectF disclosureRect(baseX, rect.Y, kDisclosureWidth * m_scale, rect.Height);
|
||||
@@ -668,7 +668,7 @@ namespace jdi
|
||||
}
|
||||
else
|
||||
{
|
||||
const SignalRef ref{row.fileIndex, row.signalIndex};
|
||||
const SignalRef ref{row.groupIndex, row.signalIndex};
|
||||
const bool badgeVisible = (m_tab != nullptr) && m_tab->IsVisible(ref);
|
||||
|
||||
// ✕ 命中优先于复选框:两者紧邻,顺序保证不会互相抢命中。
|
||||
@@ -708,7 +708,7 @@ namespace jdi
|
||||
for (std::size_t index = 0; index < m_rows.size(); ++index)
|
||||
{
|
||||
const ListRow& row = m_rows[index];
|
||||
if (row.kind == RowKind::SIGNAL && row.fileIndex == ref.fileIndex &&
|
||||
if (row.kind == RowKind::SIGNAL && row.groupIndex == ref.groupIndex &&
|
||||
row.signalIndex == ref.signalIndex)
|
||||
{
|
||||
if (top < m_scrollOffset)
|
||||
@@ -968,7 +968,7 @@ namespace jdi
|
||||
|
||||
if (m_dataSet == nullptr || m_dataSet->Empty())
|
||||
{
|
||||
UiText(g, L"尚未加载数据,请打开 CSV 文件", m_fonts->uiSmall, m_listRect, m_theme->textMuted,
|
||||
UiText(g, L"尚未加载数据,请打开数据文件", m_fonts->uiSmall, m_listRect, m_theme->textMuted,
|
||||
TextAlign::CENTER);
|
||||
}
|
||||
else if (m_rows.empty() && !m_filterText.empty())
|
||||
@@ -987,7 +987,7 @@ namespace jdi
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (m_rows[static_cast<std::size_t>(row)].kind == RowKind::FILE)
|
||||
if (m_rows[static_cast<std::size_t>(row)].kind == RowKind::GROUP)
|
||||
{
|
||||
PaintFileRow(g, row);
|
||||
}
|
||||
@@ -1025,8 +1025,8 @@ namespace jdi
|
||||
}
|
||||
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
const DataFile* file = m_dataSet->FileAt(item.fileIndex);
|
||||
if (file == nullptr)
|
||||
const SignalGroup* group = m_dataSet->GroupAt(item.groupIndex);
|
||||
if (group == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1042,7 +1042,7 @@ namespace jdi
|
||||
|
||||
const float baseX = rect.X + kPadding * m_scale;
|
||||
const float centerY = rect.Y + rect.Height * 0.5f;
|
||||
const bool collapsed = m_tab->IsCollapsed(item.fileIndex);
|
||||
const bool collapsed = m_tab->IsCollapsed(item.groupIndex);
|
||||
|
||||
// 折叠三角。
|
||||
{
|
||||
@@ -1057,15 +1057,15 @@ namespace jdi
|
||||
// 分组复选框(三态:全选 / 部分 / 未选)。
|
||||
const gd::RectF checkRect(baseX + kDisclosureWidth * m_scale, centerY - kCheckSize * m_scale * 0.5f,
|
||||
kCheckSize * m_scale, kCheckSize * m_scale);
|
||||
const bool allVisible = m_tab->FileAllVisible(item.fileIndex);
|
||||
const bool anyVisible = m_tab->FileHasVisible(item.fileIndex);
|
||||
const bool allVisible = m_tab->GroupAllVisible(item.groupIndex);
|
||||
const bool anyVisible = m_tab->GroupHasVisible(item.groupIndex);
|
||||
UiDrawCheckBox(g, checkRect, allVisible ? 1 : (anyVisible ? 2 : 0), 3.0f * m_scale);
|
||||
|
||||
const gd::RectF closeRect = FileCloseRect(row);
|
||||
|
||||
// 分组标题:左侧为“组X”标签,右侧以绘制的竖线分隔分组名(不使用字符),再显示可重命名的名称。
|
||||
const std::wstring groupLabel = L"组" + std::to_wstring(item.fileIndex + 1);
|
||||
const std::wstring stats = std::to_wstring(file->signals.size()) + L" 项";
|
||||
const std::wstring groupLabel = L"组" + std::to_wstring(item.groupIndex + 1);
|
||||
const std::wstring stats = std::to_wstring(group->signals.size()) + L" 项";
|
||||
const float groupWidth = FileGroupLabelWidth(row);
|
||||
const float statsWidth = UiMeasureTextWidth(m_fonts->uiSmall, stats);
|
||||
const float statsRight = closeRect.X - 6.0f * m_scale;
|
||||
@@ -1091,7 +1091,7 @@ namespace jdi
|
||||
}
|
||||
else
|
||||
{
|
||||
UiText(g, UiEllipsize(file->Title(), m_fonts->uiBold, nameWidth), m_fonts->uiBold,
|
||||
UiText(g, UiEllipsize(group->Title(), m_fonts->uiBold, nameWidth), m_fonts->uiBold,
|
||||
gd::RectF(nameAreaLeft, rect.Y, nameWidth, rect.Height), m_theme->textStrong,
|
||||
TextAlign::LEFT);
|
||||
}
|
||||
@@ -1116,7 +1116,7 @@ namespace jdi
|
||||
}
|
||||
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
const SignalRef ref{item.fileIndex, item.signalIndex};
|
||||
const SignalRef ref{item.groupIndex, item.signalIndex};
|
||||
const SignalData* signal = m_dataSet->Resolve(ref);
|
||||
const SignalStyle* style = StyleOf(ref);
|
||||
if (signal == nullptr || style == nullptr)
|
||||
@@ -1451,7 +1451,7 @@ namespace jdi
|
||||
}
|
||||
|
||||
const gd::RectF rect = RowRect(row);
|
||||
const bool isFile = (m_rows[static_cast<std::size_t>(row)].kind == RowKind::FILE);
|
||||
const bool isFile = (m_rows[static_cast<std::size_t>(row)].kind == RowKind::GROUP);
|
||||
const float baseX = rect.X + kPadding * m_scale + (isFile ? 0.0f : kIndent * m_scale);
|
||||
const float checkRight = baseX + kDisclosureWidth * m_scale + kCheckSize * m_scale;
|
||||
|
||||
@@ -1468,7 +1468,7 @@ namespace jdi
|
||||
{
|
||||
left = checkRight + 6.0f * m_scale + kSwatchSize * m_scale + 6.0f * m_scale;
|
||||
// 行尾的可用右界:有网格徽标时让位给徽标,否则可直接延伸到行尾 ✕ 之前。
|
||||
const SignalRef ref{m_rows[static_cast<std::size_t>(row)].fileIndex,
|
||||
const SignalRef ref{m_rows[static_cast<std::size_t>(row)].groupIndex,
|
||||
m_rows[static_cast<std::size_t>(row)].signalIndex};
|
||||
const bool badgeVisible = (m_tab != nullptr) && m_tab->IsVisible(ref) &&
|
||||
(m_tab->LaneOfSignal(ref) >= 0);
|
||||
@@ -1489,7 +1489,7 @@ namespace jdi
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
const std::wstring label = L"组" + std::to_wstring(m_rows[static_cast<std::size_t>(row)].fileIndex + 1);
|
||||
const std::wstring label = L"组" + std::to_wstring(m_rows[static_cast<std::size_t>(row)].groupIndex + 1);
|
||||
return UiMeasureTextWidth(m_fonts->uiBold, label);
|
||||
}
|
||||
|
||||
@@ -1534,7 +1534,7 @@ namespace jdi
|
||||
return false;
|
||||
}
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
const SignalData* signal = m_dataSet->Resolve(SignalRef{item.fileIndex, item.signalIndex});
|
||||
const SignalData* signal = m_dataSet->Resolve(SignalRef{item.groupIndex, item.signalIndex});
|
||||
if (signal == nullptr)
|
||||
{
|
||||
return false;
|
||||
@@ -1548,12 +1548,12 @@ namespace jdi
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const DataFile* file = m_dataSet->FileAt(m_rows[static_cast<std::size_t>(row)].fileIndex);
|
||||
if (file == nullptr)
|
||||
const SignalGroup* group = m_dataSet->GroupAt(m_rows[static_cast<std::size_t>(row)].groupIndex);
|
||||
if (group == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
initial = file->Title();
|
||||
initial = group->Title();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1638,7 +1638,7 @@ namespace jdi
|
||||
break;
|
||||
}
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
m_dataSetWritable->SetSignalDisplayName(item.fileIndex, item.signalIndex, trimmed);
|
||||
m_dataSetWritable->SetSignalDisplayName(item.groupIndex, item.signalIndex, trimmed);
|
||||
break;
|
||||
}
|
||||
case StyleEditField::FILE_NAME:
|
||||
@@ -1647,7 +1647,7 @@ namespace jdi
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_dataSetWritable->SetFileDisplayName(m_rows[static_cast<std::size_t>(row)].fileIndex, trimmed);
|
||||
m_dataSetWritable->SetGroupDisplayName(m_rows[static_cast<std::size_t>(row)].groupIndex, trimmed);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2458,12 +2458,12 @@ namespace jdi
|
||||
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
|
||||
if (item.kind == RowKind::FILE)
|
||||
if (item.kind == RowKind::GROUP)
|
||||
{
|
||||
if (part == RowPart::CHECK)
|
||||
{
|
||||
const bool allVisible = m_tab->FileAllVisible(item.fileIndex);
|
||||
m_tab->SetFileVisible(item.fileIndex, !allVisible);
|
||||
const bool allVisible = m_tab->GroupAllVisible(item.groupIndex);
|
||||
m_tab->SetGroupVisible(item.groupIndex, !allVisible);
|
||||
m_rowsDirty = true;
|
||||
return true;
|
||||
}
|
||||
@@ -2475,12 +2475,12 @@ namespace jdi
|
||||
m_pendingCloseRow = row;
|
||||
return true;
|
||||
}
|
||||
m_tab->ToggleCollapsed(item.fileIndex);
|
||||
m_tab->ToggleCollapsed(item.groupIndex);
|
||||
m_rowsDirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const SignalRef ref{item.fileIndex, item.signalIndex};
|
||||
const SignalRef ref{item.groupIndex, item.signalIndex};
|
||||
|
||||
// ✕:用户明确表示不再需要该信号 → 交由宿主从数据层移除并释放其采样内存。
|
||||
// 必须在 SelectSignal 之前处理,避免选中一条即将被删除的信号;
|
||||
@@ -2542,12 +2542,12 @@ namespace jdi
|
||||
{
|
||||
if (pendingPart == RowPart::CLOSE)
|
||||
{
|
||||
PostMessageW(m_host, WM_APP + 1, static_cast<WPARAM>(item.fileIndex), 0);
|
||||
PostMessageW(m_host, WM_APP + 1, static_cast<WPARAM>(item.groupIndex), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// wParam = 文件下标,lParam = 文件内信号下标(见 Application 的消息处理)。
|
||||
PostMessageW(m_host, WM_APP + 2, static_cast<WPARAM>(item.fileIndex),
|
||||
PostMessageW(m_host, WM_APP + 2, static_cast<WPARAM>(item.groupIndex),
|
||||
static_cast<LPARAM>(item.signalIndex));
|
||||
}
|
||||
}
|
||||
@@ -2616,7 +2616,7 @@ namespace jdi
|
||||
if (row >= 0 && part == RowPart::LANE_BADGE)
|
||||
{
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
const SignalRef ref{item.fileIndex, item.signalIndex};
|
||||
const SignalRef ref{item.groupIndex, item.signalIndex};
|
||||
if (ref == m_pendingLaneRef)
|
||||
{
|
||||
const gd::RectF badge = SignalBadgeRect(row);
|
||||
@@ -2660,11 +2660,11 @@ namespace jdi
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
|
||||
// 分组行:双击折叠三角用于折叠,双击名称区域进入重命名。
|
||||
if (item.kind == RowKind::FILE)
|
||||
if (item.kind == RowKind::GROUP)
|
||||
{
|
||||
if (part == RowPart::DISCLOSURE)
|
||||
{
|
||||
m_tab->ToggleCollapsed(item.fileIndex);
|
||||
m_tab->ToggleCollapsed(item.groupIndex);
|
||||
m_rowsDirty = true;
|
||||
return true;
|
||||
}
|
||||
@@ -2683,7 +2683,7 @@ namespace jdi
|
||||
return false;
|
||||
}
|
||||
|
||||
const SignalRef ref{item.fileIndex, item.signalIndex};
|
||||
const SignalRef ref{item.groupIndex, item.signalIndex};
|
||||
SelectSignal(ref);
|
||||
m_rowsDirty = true;
|
||||
return BeginEdit(StyleEditField::SIGNAL_NAME, row);
|
||||
@@ -2704,13 +2704,13 @@ namespace jdi
|
||||
}
|
||||
|
||||
const ListRow& item = m_rows[static_cast<std::size_t>(row)];
|
||||
if (item.kind == RowKind::FILE)
|
||||
if (item.kind == RowKind::GROUP)
|
||||
{
|
||||
OpenFileMenu(item.fileIndex, x, y);
|
||||
OpenFileMenu(item.groupIndex, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
const SignalRef ref{item.fileIndex, item.signalIndex};
|
||||
const SignalRef ref{item.groupIndex, item.signalIndex};
|
||||
SelectSignal(ref);
|
||||
OpenSignalMenu(ref, x, y);
|
||||
return true;
|
||||
@@ -2825,37 +2825,37 @@ namespace jdi
|
||||
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, x, y, items);
|
||||
}
|
||||
|
||||
void SignalPanel::OpenFileMenu(int fileIndex, int x, int y)
|
||||
void SignalPanel::OpenFileMenu(int groupIndex, int x, int y)
|
||||
{
|
||||
if (m_menu == nullptr || m_tab == nullptr || m_dataSet == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const DataFile* file = m_dataSet->FileAt(fileIndex);
|
||||
if (file == nullptr)
|
||||
const SignalGroup* group = m_dataSet->GroupAt(groupIndex);
|
||||
if (group == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_menuTargetFile = fileIndex;
|
||||
m_menuTargetFile = groupIndex;
|
||||
m_menuTargetSignal = SignalRef();
|
||||
|
||||
std::vector<MenuItem> items;
|
||||
|
||||
MenuItem header;
|
||||
header.kind = MenuItemKind::HEADER;
|
||||
header.label = file->Title();
|
||||
header.label = group->Title();
|
||||
items.push_back(header);
|
||||
|
||||
MenuItem showAll;
|
||||
showAll.label = L"选中该文件全部信号";
|
||||
showAll.id = CMD_FILE_SHOW_ALL;
|
||||
showAll.label = L"选中该分组全部信号";
|
||||
showAll.id = CMD_GROUP_SHOW_ALL;
|
||||
items.push_back(showAll);
|
||||
|
||||
MenuItem hideAll;
|
||||
hideAll.label = L"取消该文件全部信号";
|
||||
hideAll.id = CMD_FILE_HIDE_ALL;
|
||||
hideAll.label = L"取消该分组全部信号";
|
||||
hideAll.id = CMD_GROUP_HIDE_ALL;
|
||||
items.push_back(hideAll);
|
||||
|
||||
MenuItem separator;
|
||||
@@ -2863,8 +2863,8 @@ namespace jdi
|
||||
items.push_back(separator);
|
||||
|
||||
MenuItem remove;
|
||||
remove.label = L"关闭该文件(从工作区移除)";
|
||||
remove.id = CMD_FILE_REMOVE;
|
||||
remove.label = L"关闭该分组(从工作区移除)";
|
||||
remove.id = CMD_GROUP_REMOVE;
|
||||
items.push_back(remove);
|
||||
|
||||
const gd::RectF bounds = (m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area;
|
||||
@@ -3086,23 +3086,23 @@ namespace jdi
|
||||
m_rowsDirty = true;
|
||||
needRelayout = true;
|
||||
break;
|
||||
case CMD_FILE_SHOW_ALL:
|
||||
case CMD_GROUP_SHOW_ALL:
|
||||
if (m_menuTargetFile >= 0)
|
||||
{
|
||||
m_tab->SetFileVisible(m_menuTargetFile, true);
|
||||
m_tab->SetGroupVisible(m_menuTargetFile, true);
|
||||
m_rowsDirty = true;
|
||||
needRelayout = true;
|
||||
}
|
||||
break;
|
||||
case CMD_FILE_HIDE_ALL:
|
||||
case CMD_GROUP_HIDE_ALL:
|
||||
if (m_menuTargetFile >= 0)
|
||||
{
|
||||
m_tab->SetFileVisible(m_menuTargetFile, false);
|
||||
m_tab->SetGroupVisible(m_menuTargetFile, false);
|
||||
m_rowsDirty = true;
|
||||
needRelayout = true;
|
||||
}
|
||||
break;
|
||||
case CMD_FILE_REMOVE:
|
||||
case CMD_GROUP_REMOVE:
|
||||
if (m_menuTargetFile >= 0 && m_host != nullptr)
|
||||
{
|
||||
PostMessageW(m_host, WM_APP + 1, static_cast<WPARAM>(m_menuTargetFile), 0);
|
||||
|
||||
Reference in new issue
Block a user