Files
Just-DataInspector/Components/SignalPanel/Src/SignalPanelMenu.cpp
T
DeepSeek V4.1 Flash 24706c906a feat(UI): 布局边界改为四角粗/四边细包角 + 全元素覆盖 + 几何同源(RowRect O(1))
## 1. 边界观感:四角粗、四边细

用户反馈"边框就是实心矩形、粗细不一,效果不佳"。旧实现是四条等宽实心矩形
(层级 0 用 2px、其余 1px),同一元素的角与边粗细不一致,父子矩形共边时还会叠成更粗的一条。

新规则:
- 四条边固定 1 设备像素(四条独立实心矩形;不用 DrawRectangle,笔宽奇偶会偏半像素);
- 四个角额外加粗:层级 0 = 4px、其余 3px,沿水平/垂直各延伸 cornerLen(≤14px 且 ≤ 短边一半);
- 层级 > 0 整体内缩 1px,父子矩形共边不再叠粗;
- 绝不填充(几十层嵌套填充会把内容糊住);
- 整像素对齐 + PixelOffsetModeNone + 关抗锯齿。

四角粗不只是观感:多条边重合时靠角的形状分辨层次(父=青蓝大包角、子=同位置品红小包角)。

## 2. 协议:组件"收集 box",宿主统一绘制

UiComponent::CollectDebugBoxes(UiDebugBoxList&)  默认只登记 Area()
UiWidget::CollectDebugBoxes(boxes, depth)        递归整棵控件树
Application::PaintLayoutBounds                   收集后按 depth 升序一次性描边

刻意保持 box 为**值类型**而不是让它继承控件基类:PlotView 的 4 层位图缓存、
SignalPanel 的 192 槽行位图缓存都要求"绘制由组件自己负责",几何进继承体系会与
缓存键/失效体系打架。box 不参与绘制,因此能让绘制/命中/调试共用同一份几何。

## 3. 覆盖补齐(此前大量元素被裹在外层单位里)

- Toolbar:区域 / 每个按钮 / **按钮内容(文字或齿轮图标,新增 UiButtonWidget::ContentRect())**
- TabBar:区域 / 标签条 / 新建按钮 / 每个 ✕
- SignalPanel:4 分区 + 筛选输入框/清空✕/全部显示 + 滚动轨道与滑块
  + 样式区 5 行及其 12 色块/自定义/3 模式/3 调整/线宽与透明度轨道和数值
  + 目标行/名称/GX 徽标 + **每个可见行**的折起三角/复选框/色块/名称/GX 徽标/✕
  (组行另有组名/计数)+ 内联编辑框
- PlotView:区域 / 每个网格 / GX 徽标 / 装饰层矩形 / 图例托底+色块+文本
  / **5 类浮动面板全部 20+ 字段**(标题/✕/折起三角/徽标/编辑行/按钮/内容区/滚动轨道与滑块
  /透明度轨道与数值/两个开关/各行复选框与数值与滑轨与复位与名称与✕/分组栏/表头/固定点行)
- PopupMenu:浮层 / 每个条目 / 可滚动可视区 / 轨道 / 滑块 / 二级子菜单与条目
- ColorPickerPopup:面板 / 标题 / 预览 / 色号 / 色域 / 色相条 / 3 条滑轨与数值 / 输入框

标签增加**尺寸门槛**(< 56×16 逻辑像素不画):复选框/色块/✕ 紧邻,逐个小框贴字会糊成一片。

## 4. 设置按钮配色 + 齿轮图标

- ToolbarAction::SETTINGS 改为 primary = true,与「导入文件」同为强调色主按钮。
- 齿轮几何重做:齿占最外一圈(0.66R→1.0R)、外环中心线 0.54R/笔宽 0.24R、轴孔 0.17R。
  旧版让齿从环中心线伸出,齿被环的描边吃掉 ⇒ 看起来只剩一个"甜甜圈"。

## 5. 几何同源与性能

- RowRect(row) 原本每次调用都从头累加行高(O(row)),而画一行要调它 **5 次**
  (✕/徽标/名称/组计数/折起三角各一次)⇒ 200 行只见 30 行时每帧上万次加法,
  命中测试(每次鼠标移动)同样走这条路。
  新增 m_rowTop 前缀和(由 UpdateAnimGeometry() 统一重建)⇒ **O(1)**;
  长度与行集合不一致时自动退回逐行累加(正确性优先)。
- ContentOffsetBeforeRow 同样改用前缀和。
- PanelBounds() 收敛 6 处重复的 (m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area。

## 6. 验证

- make release / make debug 零 error 零 warning;TargetCheck.exe 910,336 B(889 KiB < 900 KiB)。
- .preflight/lbcheck:21 / 21 PASS(含 bounds-off-restores-pixels:关闭后逐像素回到开启前)。
- Tools/rendercheck:「工具栏带以下」区域哈希 8/8 与上一轮**逐一相同**
  (证明本轮改动只落在工具栏带内);FUNC 断言 7/7 PASS。
- Tools/uicheck:69 passed / 0 failed。

## 7. 文档

Documents/UI_Refactor_Plan.md / UI_Refactor_Progress.md / UI_Refactor_Report.md 追加第七阶段;
Documents/agent_mem.md 同步变更日志与「2026-10-28 第九轮」硬规则与踩坑记录。
2026-10-28 16:28:57 +08:00

586 lines
17 KiB
C++
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.
// SignalPanel 实现分片 —— 弹出菜单(系数/偏移、镜像、信号/分组/网格右键菜单、命令回执)。
//
// 本文件只包含 SignalPanel 的成员函数定义(机械搬移,行为不变)。
// ⚠️ 菜单是"打开即模态"的浮层:`CloseTransientUi` 必须在点击别处时收起它,
// 否则菜单会一直挂在那里吃掉后续点击。
#include "SignalPanelInternal.hpp"
namespace jdi
{
bool SignalPanel::OpenGainMenu()
{
if (m_menu == nullptr || m_tab == nullptr)
{
return false;
}
const SignalStyle* style = StyleOf(SelectedRef());
if (style == nullptr)
{
return false;
}
// 二级面板:两张“标题(带还原图标)+ 输入框”的组合:
// 1. 「系数调整」:一整行系数输入框;
// 2. 「偏移调整」:下方两行分别为「Y轴」与「时间轴」的偏移输入框。
//(与网格 Y 轴上下限的菜单内联编辑同一套做法:提交后面板保持打开,可连续微调。)
std::vector<MenuItem> items;
MenuItem gainHeader;
gainHeader.kind = MenuItemKind::HEADER_ACTION;
gainHeader.label = L"系数调整";
gainHeader.id = CMD_GAIN_RESET; // 点击标题栏右侧的还原图标 → 系数重置为 1
items.push_back(gainHeader);
// 非编辑态带正号(用户明确要求);进入编辑后的初始文本用不带符号的纯数值。
const std::wstring gainText = FormatGainText(style->gain);
const std::wstring gainEditText = FormatNumberText(static_cast<double>(style->gain));
MenuItem gainEdit;
gainEdit.kind = MenuItemKind::EDIT;
gainEdit.label.clear(); // 不显示说明文字:输入框独占整行
gainEdit.id = CMD_GAIN_EDIT;
gainEdit.value = gainText;
gainEdit.editValue = gainEditText;
gainEdit.filter = EditFilter::NUMBER;
items.push_back(gainEdit);
MenuItem offsetHeader;
offsetHeader.kind = MenuItemKind::HEADER_ACTION;
offsetHeader.label = L"偏移调整";
offsetHeader.id = CMD_OFFSET_RESET; // 点击还原图标 → 两个偏移都恢复默认值
items.push_back(offsetHeader);
// 「Y轴」:对绘制值加上一个常数偏移(波形上下移动),非编辑态带正负号。
MenuItem offsetY;
offsetY.kind = MenuItemKind::EDIT;
offsetY.label = L"Y轴";
offsetY.id = CMD_OFFSET_Y;
offsetY.value = FormatSignedNumber(static_cast<double>(style->offsetY));
offsetY.editValue = FormatNumberText(static_cast<double>(style->offsetY));
offsetY.filter = EditFilter::NUMBER;
items.push_back(offsetY);
// 「时间轴」:把采样沿时间轴平移(画在 t + offsetTime)。
// 非编辑态显示与双光标差值同一套复合单位文本,并**带正负号**(用户明确要求);
// 编辑态显示以秒为单位的数值,并在输入框最右侧提示单位 `s`。
MenuItem offsetTime;
offsetTime.kind = MenuItemKind::EDIT;
offsetTime.label = L"时间轴";
offsetTime.id = CMD_OFFSET_TIME;
offsetTime.value = FormatOffsetTimeText(style->offsetTime);
offsetTime.editValue = FormatNumberText(style->offsetTime);
offsetTime.editSuffix = L"s";
offsetTime.filter = EditFilter::NUMBER;
items.push_back(offsetTime);
// 锚定在按钮**左上角的上方一点**:二级面板**向上展开**,其左下角对准按钮左上角,
// 并再往上让出 `kAdjustMenuGap`(用户明确要求)。
//(旧实现在按钮下沿向右下展开,面板会盖住下方内容。)
const gd::RectF& button = m_adjustRects[kAdjustGain];
const gd::RectF bounds = PanelBounds();
const int anchorY =
static_cast<int>(std::lround(button.Y - kAdjustMenuGap * m_scale));
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, static_cast<int>(button.X), anchorY, items,
true);
// ⚠️ 菜单一旦打开就会接管鼠标事件,样式区不会再收到 WM_MOUSEMOVE →
// 必须在这里主动清掉按钮的悬停态,否则按钮会"像鼠标一直按着"一样持续高亮。
ClearStyleHover();
return true;
}
bool SignalPanel::OpenMirrorMenu()
{
if (m_menu == nullptr || m_tab == nullptr)
{
return false;
}
const SignalStyle* style = StyleOf(SelectedRef());
if (style == nullptr)
{
return false;
}
// 两行带复选框的选项:点击即切换、且**保持菜单打开**(可连续勾选两项)。
std::vector<MenuItem> items;
MenuItem horizontal;
horizontal.kind = MenuItemKind::NORMAL;
horizontal.label = L"水平镜像";
horizontal.id = CMD_MIRROR_HORIZONTAL;
horizontal.checked = style->mirrorHorizontal;
horizontal.keepOpen = true;
items.push_back(horizontal);
MenuItem vertical;
vertical.kind = MenuItemKind::NORMAL;
vertical.label = L"垂直镜像";
vertical.id = CMD_MIRROR_VERTICAL;
vertical.checked = style->mirrorVertical;
vertical.keepOpen = true;
items.push_back(vertical);
const gd::RectF& button = m_adjustRects[kAdjustMirror];
const gd::RectF bounds = PanelBounds();
// 与「系数/偏移」面板同一套锛定:向上展开,左下角 = 按钮左上角再上移一点。
const int anchorY =
static_cast<int>(std::lround(button.Y - kAdjustMenuGap * m_scale));
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, static_cast<int>(button.X), anchorY, items,
true);
ClearStyleHover();
return true;
}
void SignalPanel::ClearStyleHover()
{
if (m_hoverStyle == StyleHit::NONE && m_hoverStyleIndex < 0)
{
return;
}
m_hoverStyle = StyleHit::NONE;
m_hoverStyleIndex = -1;
}
bool SignalPanel::CloseTransientUi()
{
bool changed = false;
if (m_editField != StyleEditField::NONE)
{
changed = EndEdit(true) || changed;
}
// 点击面板之外也要结束「筛选信号」的编辑状态。
if (m_filterEditing)
{
changed = EndFilterEdit() || changed;
}
if (m_colorPicker != nullptr && m_colorPicker->IsOpen())
{
m_colorPicker->Close();
changed = true;
}
// 面板外点击:取消尚未抬起确认的关闭操作。
m_pendingClosePart = RowPart::NONE;
m_pendingCloseRow = -1;
m_pendingFilterClear = false;
m_pendingGainMenu = false;
m_pendingMirrorMenu = false;
m_pendingTargetLaneMenu = false;
return changed;
}
void SignalPanel::OpenSignalMenu(const SignalRef& ref, int x, int y)
{
if (m_menu == nullptr || m_tab == nullptr || m_dataSet == nullptr)
{
return;
}
const SignalStyle* style = StyleOf(ref);
const SignalData* signal = m_dataSet->Resolve(ref);
if (style == nullptr || signal == nullptr)
{
return;
}
m_menuTargetSignal = ref;
m_menuTargetGroup = -1;
std::vector<MenuItem> items;
MenuItem header;
header.kind = MenuItemKind::HEADER;
header.label = signal->name;
items.push_back(header);
MenuItem toggle;
toggle.label = m_tab->IsVisible(ref) ? L"隐藏该信号" : L"显示该信号";
toggle.id = CMD_SIGNAL_TOGGLE;
items.push_back(toggle);
MenuItem solo;
solo.label = L"仅显示该信号";
solo.id = CMD_SIGNAL_SOLO;
items.push_back(solo);
MenuItem showAll;
showAll.label = L"显示全部信号";
showAll.id = CMD_SIGNAL_SHOW_ALL;
items.push_back(showAll);
MenuItem hideAll;
hideAll.label = L"隐藏全部信号";
hideAll.id = CMD_SIGNAL_HIDE_ALL;
items.push_back(hideAll);
MenuItem separator;
separator.kind = MenuItemKind::SEPARATOR;
items.push_back(separator);
int paletteCount = 0;
const std::uint32_t* palette = SignalPalette(paletteCount);
MenuItem colors;
colors.kind = MenuItemKind::SWATCHES;
colors.id = CMD_COLOR_BASE;
colors.swatchCount = std::min(paletteCount, 16);
colors.checkedSwatch = -1;
for (int index = 0; index < colors.swatchCount; ++index)
{
colors.swatches[index] = palette[index];
if (style->color == palette[index])
{
colors.checkedSwatch = index;
}
}
items.push_back(colors);
MenuItem modeHeader;
modeHeader.kind = MenuItemKind::HEADER;
modeHeader.label = L"样式";
items.push_back(modeHeader);
for (int index = 0; index < kModeOptionCount; ++index)
{
MenuItem mode;
mode.label = DrawModeName(kModeOptions[index]);
mode.id = CMD_MODE_BASE + index;
mode.checked = (style->mode == kModeOptions[index]);
items.push_back(mode);
}
const gd::RectF bounds = PanelBounds();
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, x, y, items);
}
void SignalPanel::OpenGroupMenu(int groupIndex, int x, int y)
{
if (m_menu == nullptr || m_tab == nullptr || m_dataSet == nullptr)
{
return;
}
const SignalGroup* group = m_dataSet->GroupAt(groupIndex);
if (group == nullptr)
{
return;
}
m_menuTargetGroup = groupIndex;
m_menuTargetSignal = SignalRef();
std::vector<MenuItem> items;
MenuItem header;
header.kind = MenuItemKind::HEADER;
header.label = group->name;
items.push_back(header);
MenuItem showAll;
showAll.label = L"选中该分组全部信号";
showAll.id = CMD_GROUP_SHOW_ALL;
items.push_back(showAll);
MenuItem hideAll;
hideAll.label = L"取消该分组全部信号";
hideAll.id = CMD_GROUP_HIDE_ALL;
items.push_back(hideAll);
MenuItem separator;
separator.kind = MenuItemKind::SEPARATOR;
items.push_back(separator);
MenuItem remove;
remove.label = L"关闭该分组(从工作区移除)";
remove.id = CMD_GROUP_REMOVE;
items.push_back(remove);
const gd::RectF bounds = PanelBounds();
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, x, y, items);
}
void SignalPanel::OpenLaneMenu(const SignalRef& ref, int x, int y)
{
if (m_menu == nullptr || m_tab == nullptr || m_dataSet == nullptr)
{
return;
}
const SignalData* signal = m_dataSet->Resolve(ref);
if (signal == nullptr)
{
return;
}
m_menuTargetSignal = ref;
m_menuTargetGroup = -1;
std::vector<MenuItem> items;
MenuItem header;
header.kind = MenuItemKind::HEADER;
header.label = L"所属网格";
items.push_back(header);
const std::vector<Lane>& lanes = m_tab->Lanes();
const int currentLane = m_tab->LaneOfSignal(ref);
for (std::size_t index = 0; index < lanes.size(); ++index)
{
MenuItem lane;
lane.label = L"网格 " + std::to_wstring(index + 1) + L"(" +
std::to_wstring(lanes[index].signals.size()) + L" 条信号)";
lane.id = CMD_LANE_BASE + static_cast<int>(index);
lane.checked = (currentLane == static_cast<int>(index));
items.push_back(lane);
}
MenuItem separator;
separator.kind = MenuItemKind::SEPARATOR;
items.push_back(separator);
MenuItem create;
create.label = L"新建网格";
create.id = CMD_LANE_NEW;
items.push_back(create);
const gd::RectF bounds = PanelBounds();
// ⚠️ 网格可能是**上百个**(例如把 MF4 的全部信号各自放进独立网格):
// 不限制高度时菜单会长到屏幕之外、且逐条绘制上百行会明显卡顿。
// 这里限制可滚动条目区**最多显示 10 行**(用户 2026-10-16 要求):
// 超出部分用滚轮 / 右侧滚动条翻动,而顶部「所属网格」标题与底部的
// 「新建网格」**始终固定不动**。
MenuLayoutConstraint constraint;
constraint.maxScrollRows = kLaneMenuVisibleRows;
// 额外加一道硬上限:窗口很矮时也不得超出客户区(与行数上限取更严格者)。
constraint.maxHeight = std::max(180.0f, bounds.Height * 0.8f);
m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, x, y, items, false, constraint);
}
bool SignalPanel::OnMenuCommand(const MenuResult& result)
{
if (m_tab == nullptr || m_dataSet == nullptr || !result.valid)
{
return false;
}
// 「系数」/「偏移」二级面板里的输入框提交:就地更新,面板保持打开
//(与网格 Y 轴上下限的菜单内联编辑一致),并把夹取后的真实值回写到输入框上。
if (result.editCommit)
{
SignalStyle* style = StyleOf(SelectedRef());
if (style != nullptr)
{
const std::wstring trimmed = Trim(result.editText);
wchar_t* stop = nullptr;
const double parsed =
trimmed.empty() ? 0.0 : std::wcstod(trimmed.c_str(), &stop);
// 非法/空输入保持原值(只把真实值回写到输入框,避免用户看到错误的数字)。
const bool valid = !trimmed.empty() && stop != trimmed.c_str() && std::isfinite(parsed);
switch (result.id)
{
case CMD_GAIN_EDIT:
{
if (valid)
{
style->gain =
static_cast<float>(std::max(-kGainLimit, std::min(parsed, kGainLimit)));
}
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain),
FormatNumberText(static_cast<double>(style->gain)));
}
return false;
}
case CMD_OFFSET_Y:
{
if (valid)
{
style->offsetY = static_cast<float>(
std::max(-static_cast<double>(kGainLimit), std::min(parsed, kGainLimit)));
}
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(
CMD_OFFSET_Y, FormatSignedNumber(static_cast<double>(style->offsetY)),
FormatNumberText(static_cast<double>(style->offsetY)));
}
return false;
}
case CMD_OFFSET_TIME:
{
if (valid)
{
style->offsetTime =
std::max(-kGainLimit, std::min(parsed, kGainLimit));
}
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_OFFSET_TIME,
FormatOffsetTimeText(style->offsetTime),
FormatNumberText(style->offsetTime));
}
return false;
}
default:
break;
}
}
else
{
return false;
}
}
// 标题栏右侧的「还原」图标:系数重置为默认值 1(面板保持打开,输入框立即回显)。
if (result.id == CMD_GAIN_RESET)
{
if (SignalStyle* style = StyleOf(SelectedRef()))
{
style->gain = 1.0f;
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain),
FormatNumberText(static_cast<double>(style->gain)));
}
}
return false;
}
// 「偏移调整」的「还原」图标:Y 与时间偏移都恢复为默认值 0。
if (result.id == CMD_OFFSET_RESET)
{
if (SignalStyle* style = StyleOf(SelectedRef()))
{
style->offsetY = 0.0f;
style->offsetTime = 0.0;
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_OFFSET_Y, FormatSignedNumber(0.0),
FormatNumberText(0.0));
m_menu->UpdateItemValue(CMD_OFFSET_TIME, FormatOffsetTimeText(0.0),
FormatNumberText(0.0));
}
}
return false;
}
// 「镜像」二级面板:两行带复选框的切换项(面板保持打开,勾选状态就地回写)。
if (result.id == CMD_MIRROR_HORIZONTAL || result.id == CMD_MIRROR_VERTICAL)
{
if (SignalStyle* style = StyleOf(SelectedRef()))
{
const bool horizontal = (result.id == CMD_MIRROR_HORIZONTAL);
bool& flag = horizontal ? style->mirrorHorizontal : style->mirrorVertical;
flag = !flag;
if (!horizontal)
{
// ⚠️ Y 偏移以**未镜像空间**为准:垂直镜像开关变化时同步取反偏移量,
// 使绘制等价于「先加上偏移再镜像」(用户明确要求)。
style->offsetY = -style->offsetY;
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(
CMD_OFFSET_Y, FormatSignedNumber(static_cast<double>(style->offsetY)),
FormatNumberText(static_cast<double>(style->offsetY)));
}
}
if (m_menu != nullptr)
{
m_menu->UpdateItemLabel(result.id, horizontal ? L"水平镜像" : L"垂直镜像", flag, true);
}
}
return false;
}
bool needRelayout = false;
switch (result.id)
{
case CMD_SIGNAL_TOGGLE:
if (m_menuTargetSignal.IsValid())
{
m_tab->ToggleVisible(m_menuTargetSignal);
m_rowsDirty = true;
needRelayout = true;
}
break;
case CMD_SIGNAL_SOLO:
if (m_menuTargetSignal.IsValid())
{
m_tab->SoloSignal(m_menuTargetSignal);
m_rowsDirty = true;
needRelayout = true;
}
break;
case CMD_SIGNAL_SHOW_ALL:
m_tab->SetAllVisible(true);
m_rowsDirty = true;
needRelayout = true;
break;
case CMD_SIGNAL_HIDE_ALL:
m_tab->SetAllVisible(false);
m_rowsDirty = true;
needRelayout = true;
break;
case CMD_GROUP_SHOW_ALL:
if (m_menuTargetGroup >= 0)
{
m_tab->SetGroupVisible(m_menuTargetGroup, true);
m_rowsDirty = true;
needRelayout = true;
}
break;
case CMD_GROUP_HIDE_ALL:
if (m_menuTargetGroup >= 0)
{
m_tab->SetGroupVisible(m_menuTargetGroup, false);
m_rowsDirty = true;
needRelayout = true;
}
break;
case CMD_GROUP_REMOVE:
if (m_menuTargetGroup >= 0 && m_host != nullptr)
{
PostMessageW(m_host, WM_APP + 1, static_cast<WPARAM>(m_menuTargetGroup), 0);
}
break;
case CMD_LANE_NEW:
if (m_menuTargetSignal.IsValid())
{
m_tab->MoveSignalToLane(*m_dataSet, m_menuTargetSignal, -1);
m_rowsDirty = true;
needRelayout = true;
}
break;
default:
if (result.id >= CMD_LANE_BASE && result.id < CMD_LANE_NEW)
{
if (m_menuTargetSignal.IsValid())
{
m_tab->MoveSignalToLane(*m_dataSet, m_menuTargetSignal, result.id - CMD_LANE_BASE);
m_rowsDirty = true;
needRelayout = true;
}
}
else if (result.id == CMD_COLOR_BASE && result.swatchIndex >= 0)
{
if (SignalStyle* style = StyleOf(m_menuTargetSignal))
{
int paletteCount = 0;
const std::uint32_t* palette = SignalPalette(paletteCount);
style->color = palette[result.swatchIndex % std::max(1, paletteCount)];
}
}
else if (result.id >= CMD_MODE_BASE && result.id < CMD_MODE_BASE + kModeOptionCount)
{
if (SignalStyle* style = StyleOf(m_menuTargetSignal))
{
style->mode = kModeOptions[result.id - CMD_MODE_BASE];
}
}
break;
}
return needRelayout;
}
} // namespace jdi