diff --git a/Components/SignalPanel/Inc/SignalPanel.hpp b/Components/SignalPanel/Inc/SignalPanel.hpp index 06ed407..f2f6532 100644 --- a/Components/SignalPanel/Inc/SignalPanel.hpp +++ b/Components/SignalPanel/Inc/SignalPanel.hpp @@ -34,7 +34,8 @@ namespace jdi CMD_COLOR_BASE = 100, /**< 预设颜色基准(配合色块下标)。 */ CMD_MODE_BASE = 200, /**< 绘制样式基准(配合下标)。 */ CMD_LANE_BASE = 300, /**< 网格归属基准(配合下标)。 */ - CMD_LANE_NEW = 400 /**< 新建网格。 */ + CMD_LANE_NEW = 400, /**< 新建网格。 */ + CMD_GAIN_EDIT = 500, /**< 「系数调整」二级面板里的系数输入框(幂等提交)。 */ }; /** @@ -300,8 +301,7 @@ namespace jdi WIDTH = 1, /**< 线宽数值。 */ OPACITY = 2, /**< 不透明度数值(百分比)。 */ SIGNAL_NAME = 3, /**< 信号显示名称(列表行内)。 */ - FILE_NAME = 4, /**< 分组显示名称(列表行内)。 */ - GAIN = 5 /**< 系数增益(绘制前把采样值乘以该系数)。 */ + FILE_NAME = 4 /**< 分组显示名称(列表行内)。 */ }; /** @@ -340,7 +340,7 @@ namespace jdi OPACITY_VALUE, /**< 不透明度数值框(可点击输入)。 */ WIDTH_SLIDER, /**< 线宽滑块。 */ WIDTH_VALUE, /**< 线宽数值框(可点击输入)。 */ - GAIN_FIELD, /**< 系数增益输入框。 */ + GAIN_ADJUST, /**< 「系数调整」按钮(点击展开系数输入二级面板)。 */ ABSOLUTE_TOGGLE, /**< 「取绝对值」切换按钮。 */ INVERT_TOGGLE, /**< 「中心反转」切换按钮。 */ TOGGLE_ALL, /**< 全部显示/隐藏按钮。 */ @@ -493,6 +493,17 @@ namespace jdi */ bool OpenColorPickerPopup(const SignalStyle& style); + /** + * @brief 打开「系数调整」的**二级面板**(一个带输入框的浮动面板,锚定在该按钮下方)。 + * + * 与自定义颜色入口同一套交互:点击按钮 → 抬起时展开浮动面板 → 面板内的输入框可编辑 + * 绘制前乘上的系数(默认 1),回车 / 点击其它位置提交,面板保持打开(与网格 Y 轴上下限 + * 的菜单内联输入框一致)。 + * + * @return 面板已打开返回 true。 + */ + bool OpenGainMenu(); + /** * @brief 判断当前标签页中全部信号是否均为可见状态。 * @return 全部可见且至少有一条信号返回 true。 @@ -596,17 +607,16 @@ namespace jdi std::array m_modeRects; /**< 绘制样式按钮矩形。 */ - // 样式区固定六行:颜色 / 样式 / 线宽 / 透明度 / 系数增益 / 取绝对值与中心反转。 + // 样式区固定五行:颜色 / 样式 / 调整(系数调整·取绝对值·中心反转)/ 线宽 / 透明度。 // 各行控件共用同一内容列宽度与左边界。 - std::array m_styleRowRects; /**< 六行整体矩形(含标签列)。 */ + std::array m_styleRowRects; /**< 五行整体矩形(含标签列)。 */ gd::RectF m_opacityTrackRect; /**< 不透明度滑块轨道矩形。 */ gd::RectF m_opacityValueRect; /**< 不透明度数值文本矩形。 */ gd::RectF m_widthTrackRect; /**< 线宽滑块轨道矩形。 */ gd::RectF m_widthValueRect; /**< 线宽数值文本矩形(双击可输入)。 */ - gd::RectF m_gainFieldRect; /**< 系数增益输入框矩形。 */ - gd::RectF m_absoluteRect; /**< 「取绝对值」切换按钮矩形。 */ - gd::RectF m_invertedRect; /**< 「中心反转」切换按钮矩形。 */ + /** 「调整」行的三个等宽按钮:0 = 系数调整,1 = 取绝对值,2 = 中心反转。 */ + std::array m_adjustRects; std::vector m_rows; /**< 列表行集合(惰性缓存)。 */ bool m_rowsDirty = true; /**< 列表行集合是否需要重建。 */ @@ -648,6 +658,7 @@ namespace jdi bool m_pendingLaneMenu = false; /**< 左键抬起时是否弹出网格归属菜单。 */ SignalRef m_pendingLaneRef; /**< 待弹出菜单的目标信号。 */ bool m_pendingColorPicker = false; /**< 左键抬起时是否弹出拾色器。 */ + bool m_pendingGainMenu = false; /**< 左键抬起时是否弹出「系数调整」的系数输入二级面板。 */ /** * @brief 待确认的「关闭类」按钮(✕)。 diff --git a/Components/SignalPanel/Src/SignalPanel.cpp b/Components/SignalPanel/Src/SignalPanel.cpp index 4f1696b..ddc26a0 100644 --- a/Components/SignalPanel/Src/SignalPanel.cpp +++ b/Components/SignalPanel/Src/SignalPanel.cpp @@ -88,15 +88,15 @@ namespace jdi return p == pattern.size(); } - // 样式编辑区:固定六行网格(颜色 / 样式 / 线宽 / 透明度 / 系数增益 / 取绝对值与中心反转), + // 样式编辑区:固定五行网格(颜色 / 样式 / 调整 / 线宽 / 透明度), // 行高、间距与内容列宽度统一,保证面板宽度变化时各行控件始终对齐,且不会被无限制拉伸。 const float kStylePadH = 12.0f; /**< 样式区左右内边距。 */ const float kStylePadV = 12.0f; /**< 样式区上下内边距。 */ const float kStyleRowHeight = 26.0f; /**< 样式区单行高度。 */ const float kStyleRowGap = 10.0f; /**< 样式区行间距。 */ - const float kStyleLabelWidth = 56.0f; /**< 样式行标签列宽度(需容纳「系数增益」四个字)。 */ + const float kStyleLabelWidth = 44.0f; /**< 样式行标签列宽度。 */ const float kStyleLabelGap = 8.0f; /**< 标签列与内容列间距。 */ - const float kStyleRowCount = 6.0f; /**< 样式区固定行数。 */ + const float kStyleRowCount = 5.0f; /**< 样式区固定行数。 */ const float kStyleContentMax = 260.0f; /**< 内容列最大宽度(再拉宽面板也不再延伸)。 */ const float kSwatchEdge = 18.0f; /**< 颜色色块边长上限(含自定义颜色入口)。 */ const float kSwatchMin = 11.0f; /**< 颜色色块边长下限。 */ @@ -105,16 +105,26 @@ namespace jdi const float kSliderValueWidth = 52.0f; /**< 滑块行数值文本宽度(透明度与线宽一致)。 */ const float kSliderValueGap = 8.0f; /**< 滑块轨道与数值文本间距。 */ const float kTrackHeight = 6.0f; /**< 滑块轨道高度。 */ - const float kGainFieldWidth = 110.0f; /**< 系数增益输入框宽度(逻辑像素)。 */ - const float kGainFieldPadH = 6.0f; /**< 系数增益输入框内文字左右内缩。 */ - const float kStyleFlagGap = 6.0f; /**< 「取绝对值」与「中心反转」两个按钮之间的间距。 */ + const float kAdjustGap = 6.0f; /**< 「调整」行三个按钮之间的间距。 */ + + /** 「调整」行的按钮下标(与 `m_adjustRects` 同序)。 */ + const int kAdjustGain = 0; /**< 系数调整(点击展开系数输入二级面板)。 */ + const int kAdjustAbsolute = 1; /**< 取绝对值。 */ + const int kAdjustInvert = 2; /**< 中心反转。 */ + const int kAdjustCount = 3; /**< 按钮数量。 */ const float kWidthMin = 0.5f; /**< 允许的最小线宽(px)。 */ const float kWidthMax = 10.0f; /**< 允许的最大线宽(px)。 */ - /** @brief 系数增益允许的绝对值上限(防止输入极端值把波形完全挤出可视区)。 */ + /** @brief 系数调整允许的绝对值上限(防止输入极端值把波形完全挤出可视区)。 */ const double kGainLimit = 10000.0; + /** @brief 系数为默认值 1(不做任何缩放)时按钮显示为未启用样式。 */ + bool GainIsDefault(float gain) + { + return std::fabs(gain - 1.0f) < 1.0e-6f; + } + /** @brief 可选绘制样式顺序(不含阶梯方式)。 */ const DrawMode kModeOptions[] = {DrawMode::LINE, DrawMode::SCATTER, DrawMode::LINE_SCATTER}; const int kModeOptionCount = static_cast(sizeof(kModeOptions) / sizeof(kModeOptions[0])); @@ -188,6 +198,7 @@ namespace jdi { m_colorRects.fill(gd::RectF()); m_modeRects.fill(gd::RectF()); + m_adjustRects.fill(gd::RectF()); } void SignalPanel::Setup(HWND host, const UiFonts* fonts, const Theme* theme, PopupMenu* menu, @@ -214,6 +225,7 @@ namespace jdi m_editField = StyleEditField::NONE; m_pendingLaneMenu = false; m_pendingColorPicker = false; + m_pendingGainMenu = false; m_pendingClosePart = RowPart::NONE; m_pendingCloseRow = -1; m_pendingFilterClear = false; @@ -291,11 +303,12 @@ namespace jdi return LoadCursorW(nullptr, IDC_HAND); } - // 样式区的可点击控件(系数增益输入框、两个切换按钮、数值框)也用手型光标。 + // 样式区的可点击控件(「调整」行三个按钮、线宽/透明度数值框)也用手型光标。 const float fx = static_cast(x); const float fy = static_cast(y); - if (UiPointInRect(m_gainFieldRect, fx, fy) || UiPointInRect(m_absoluteRect, fx, fy) || - UiPointInRect(m_invertedRect, fx, fy) || UiPointInRect(m_widthValueRect, fx, fy) || + if (UiPointInRect(m_adjustRects[kAdjustGain], fx, fy) || + UiPointInRect(m_adjustRects[kAdjustAbsolute], fx, fy) || + UiPointInRect(m_adjustRects[kAdjustInvert], fx, fy) || UiPointInRect(m_widthValueRect, fx, fy) || UiPointInRect(m_opacityValueRect, fx, fy)) { return LoadCursorW(nullptr, IDC_HAND); @@ -660,7 +673,7 @@ namespace jdi const float rowStep = (kStyleRowHeight + kStyleRowGap) * m_scale; const float firstTop = m_styleRect.Y + kStylePadV * m_scale; - // 内容列宽度带上下限:面板变窄时收缩,面板变宽时不再延伸,四行始终共用同一宽度。 + // 内容列宽度带上下限:面板变窄时收缩,面板变宽时不再延伸,五行始终共用同一宽度。 const float available = std::max(40.0f, m_styleRect.Width - padH * 2.0f - labelWidth - labelGap); const float contentWidth = std::min(available, kStyleContentMax * m_scale); @@ -677,10 +690,9 @@ namespace jdi const gd::RectF& colorRow = m_styleRowRects[0]; const gd::RectF& modeRow = m_styleRowRects[1]; - const gd::RectF& widthRow = m_styleRowRects[2]; - const gd::RectF& opacityRow = m_styleRowRects[3]; - const gd::RectF& gainRow = m_styleRowRects[4]; - const gd::RectF& flagRow = m_styleRowRects[5]; + const gd::RectF& adjustRow = m_styleRowRects[2]; + const gd::RectF& widthRow = m_styleRowRects[3]; + const gd::RectF& opacityRow = m_styleRowRects[4]; // ---- 颜色行:预设色块 + 同尺寸自定义颜色入口(合计恰好铺满内容列宽度)---- int paletteCount = 0; @@ -708,7 +720,7 @@ namespace jdi } m_colorCount = std::max(1, count); - // 颜色行与其余三行共用同一左边界(不再居中),保证四行纵向对齐。 + // 颜色行与其余各行共用同一左边界(不再居中),保证五行纵向对齐。 const float colorRowLeft = contentLeft; const float swatchTop = colorRow.Y + (rowHeight - swatchSize) * 0.5f; @@ -734,6 +746,18 @@ namespace jdi modeWidth, rowHeight); } + // ---- 「调整」行:系数调整 / 取绝对值 / 中心反转(三个铺满内容列的等宽按钮)---- + const float adjustGap = kAdjustGap * m_scale; + const float adjustWidth = + std::max(20.0f, (contentWidth - adjustGap * static_cast(kAdjustCount - 1)) / + static_cast(kAdjustCount)); + for (int index = 0; index < kAdjustCount; ++index) + { + m_adjustRects[static_cast(index)] = + gd::RectF(contentLeft + static_cast(index) * (adjustWidth + adjustGap), adjustRow.Y, + adjustWidth, rowHeight); + } + // ---- 线宽行与透明度行:几何完全一致,保证纵向宽度对齐(线宽在上、透明度在下)---- const float valueWidth = std::min(kSliderValueWidth * m_scale, contentWidth * 0.4f); const float trackWidth = std::max(24.0f, contentWidth - valueWidth - kSliderValueGap * m_scale); @@ -748,16 +772,6 @@ namespace jdi trackWidth, trackHeight); m_opacityValueRect = gd::RectF(contentLeft + trackWidth + kSliderValueGap * m_scale, opacityRow.Y, valueWidth, rowHeight); - - // ---- 系数增益行:靠左的数值输入框 ---- - const float gainWidth = std::min(kGainFieldWidth * m_scale, contentWidth); - m_gainFieldRect = gd::RectF(contentLeft, gainRow.Y, gainWidth, rowHeight); - - // ---- 「取绝对值」与「中心反转」:同一行的两个等宽切换按钮 ---- - const float flagGap = kStyleFlagGap * m_scale; - const float flagWidth = std::max(20.0f, (contentWidth - flagGap) * 0.5f); - m_absoluteRect = gd::RectF(contentLeft, flagRow.Y, flagWidth, rowHeight); - m_invertedRect = gd::RectF(contentLeft + flagWidth + flagGap, flagRow.Y, flagWidth, rowHeight); } void SignalPanel::Layout(const gd::RectF& area) @@ -1262,9 +1276,31 @@ namespace jdi } } - // ---- 第三行:线宽(数值可点击输入)---- + // ---- 第三行:调整(系数调整 / 取绝对值 / 中心反转,三个切换类按钮)---- + // 三者依次作用于绘制值:系数(点击展开二级面板输入)→ 取绝对值 → 中心反转。 { - paintRowLabel(2, L"线宽"); + paintRowLabel(2, L"调整"); + + const auto paintToggle = [&](int index, const wchar_t* label, bool checked, StyleHit target) + { + const gd::RectF& rect = m_adjustRects[static_cast(index)]; + const bool hot = (m_hoverStyle == target); + UiFillRoundRect(g, rect, kCorner * m_scale, + checked ? m_theme->accentSoft : (hot ? m_theme->hover : m_theme->cardBg)); + UiDrawRoundRect(g, rect, kCorner * m_scale, + checked ? m_theme->accent : m_theme->border, 1.0f); + UiText(g, label, m_fonts->uiSmall, rect, + checked ? m_theme->textStrong : m_theme->text, TextAlign::CENTER); + }; + // 系数调整:仅当系数**不等于默认值 1** 时才显示为"已启用"(与其它切换按钮的启用样式一致)。 + paintToggle(kAdjustGain, L"系数调整", !GainIsDefault(style->gain), StyleHit::GAIN_ADJUST); + paintToggle(kAdjustAbsolute, L"取绝对值", style->absolute, StyleHit::ABSOLUTE_TOGGLE); + paintToggle(kAdjustInvert, L"中心反转", style->inverted, StyleHit::INVERT_TOGGLE); + } + + // ---- 第四行:线宽(数值可点击输入)---- + { + paintRowLabel(3, L"线宽"); const double ratio = (static_cast(style->lineWidth) - kWidthMin) / (kWidthMax - kWidthMin); @@ -1289,9 +1325,9 @@ namespace jdi } } - // ---- 第四行:透明度(数值可点击输入)---- + // ---- 第五行:透明度(数值可点击输入)---- { - paintRowLabel(3, L"透明度"); + paintRowLabel(4, L"透明度"); const bool active = m_draggingOpacity || (m_hoverStyle == StyleHit::OPACITY_SLIDER); UiDrawSlider(g, m_opacityTrackRect, static_cast(style->opacity), m_theme->accent, active, @@ -1308,45 +1344,6 @@ namespace jdi m_theme->textStrong, TextAlign::RIGHT); } } - - // ---- 第五行:系数增益(绘制前把采样值乘以该系数,可点击输入)---- - { - paintRowLabel(4, L"系数增益"); - - if (m_editField == StyleEditField::GAIN) - { - UiDrawEditField(g, m_gainFieldRect, m_edit, m_fonts->monoSmall, m_scale); - } - else - { - const bool hovered = (m_hoverStyle == StyleHit::GAIN_FIELD); - UiFillRoundRect(g, m_gainFieldRect, kCorner * m_scale, m_theme->cardBg); - UiDrawRoundRect(g, m_gainFieldRect, kCorner * m_scale, - hovered ? m_theme->accent : m_theme->border, hovered ? 1.6f : 1.0f); - const gd::RectF textRect(m_gainFieldRect.X + kGainFieldPadH * m_scale, m_gainFieldRect.Y, - std::max(8.0f, m_gainFieldRect.Width - kGainFieldPadH * 2.0f * m_scale), - m_gainFieldRect.Height); - UiText(g, FormatGainText(style->gain), m_fonts->monoSmall, textRect, m_theme->textStrong, - TextAlign::LEFT); - } - } - - // ---- 第六行:取绝对值 / 中心反转(两个切换按钮,作用顺序为「先绝对值、后中心反转」)---- - { - const auto paintFlag = [&](const gd::RectF& rect, const wchar_t* label, bool checked, - StyleHit target) - { - const bool hot = (m_hoverStyle == target); - UiFillRoundRect(g, rect, kCorner * m_scale, - checked ? m_theme->accentSoft : (hot ? m_theme->hover : m_theme->cardBg)); - UiDrawRoundRect(g, rect, kCorner * m_scale, - checked ? m_theme->accent : m_theme->border, 1.0f); - UiText(g, label, m_fonts->uiSmall, rect, - checked ? m_theme->textStrong : m_theme->text, TextAlign::CENTER); - }; - paintFlag(m_absoluteRect, L"取绝对值", style->absolute, StyleHit::ABSOLUTE_TOGGLE); - paintFlag(m_invertedRect, L"中心反转", style->inverted, StyleHit::INVERT_TOGGLE); - } } bool SignalPanel::AreAllSignalsVisible() const @@ -1466,16 +1463,6 @@ namespace jdi initial = std::to_wstring(static_cast(style->opacity * 100.0f + 0.5f)); break; } - case StyleEditField::GAIN: - { - const SignalStyle* style = StyleOf(SelectedRef()); - if (style == nullptr) - { - return false; - } - initial = FormatGainText(style->gain); - break; - } case StyleEditField::SIGNAL_NAME: { if (row < 0 || row >= static_cast(m_rows.size())) @@ -1580,25 +1567,6 @@ namespace jdi } break; } - case StyleEditField::GAIN: - { - if (trimmed.empty()) - { - break; - } - wchar_t* stop = nullptr; - const double parsed = std::wcstod(trimmed.c_str(), &stop); - if (stop == trimmed.c_str() || !std::isfinite(parsed)) - { - break; // 非法输入保持原值 - } - if (SignalStyle* style = StyleOf(SelectedRef())) - { - style->gain = - static_cast(std::max(-kGainLimit, std::min(parsed, kGainLimit))); - } - break; - } case StyleEditField::SIGNAL_NAME: { if (row < 0 || row >= static_cast(m_rows.size()) || m_dataSetWritable == nullptr) @@ -1653,8 +1621,6 @@ namespace jdi return m_widthValueRect; case StyleEditField::OPACITY: return m_opacityValueRect; - case StyleEditField::GAIN: - return m_gainFieldRect; case StyleEditField::SIGNAL_NAME: case StyleEditField::FILE_NAME: return RowNameRect(m_editRow); @@ -1999,6 +1965,43 @@ namespace jdi return true; } + bool SignalPanel::OpenGainMenu() + { + if (m_menu == nullptr || m_tab == nullptr) + { + return false; + } + const SignalStyle* style = StyleOf(SelectedRef()); + if (style == nullptr) + { + return false; + } + + // 二级面板:一个标题行 + 一个内联输入框(与网格 Y 轴上下限的菜单内联编辑同一套做法, + // 提交后面板保持打开,因此可以连续微调;点击面板外或回车均会提交)。 + std::vector items; + + MenuItem header; + header.kind = MenuItemKind::HEADER; + header.label = L"系数调整"; + items.push_back(header); + + MenuItem edit; + edit.kind = MenuItemKind::EDIT; + edit.label = L"绘制系数"; + edit.id = CMD_GAIN_EDIT; + edit.value = FormatGainText(style->gain); + edit.filter = EditFilter::NUMBER; + items.push_back(edit); + + // 锚定在按钮下沿:面板紧贴按钮展开(与拾色器"以触发器矩形为锚点"的观感一致)。 + const gd::RectF& button = m_adjustRects[kAdjustGain]; + const gd::RectF bounds = (m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area; + m_menu->Open(MenuScope::SIGNAL_PANEL, bounds, static_cast(button.X), + static_cast(button.Y + button.Height), items); + return true; + } + bool SignalPanel::CloseTransientUi() { bool changed = false; @@ -2020,6 +2023,7 @@ namespace jdi m_pendingClosePart = RowPart::NONE; m_pendingCloseRow = -1; m_pendingFilterClear = false; + m_pendingGainMenu = false; return changed; } @@ -2111,15 +2115,15 @@ namespace jdi { hoverStyle = StyleHit::WIDTH_VALUE; } - if (hoverStyle == StyleHit::NONE && UiPointInRect(m_gainFieldRect, fx, fy)) + if (hoverStyle == StyleHit::NONE && UiPointInRect(m_adjustRects[kAdjustGain], fx, fy)) { - hoverStyle = StyleHit::GAIN_FIELD; + hoverStyle = StyleHit::GAIN_ADJUST; } - if (hoverStyle == StyleHit::NONE && UiPointInRect(m_absoluteRect, fx, fy)) + if (hoverStyle == StyleHit::NONE && UiPointInRect(m_adjustRects[kAdjustAbsolute], fx, fy)) { hoverStyle = StyleHit::ABSOLUTE_TOGGLE; } - if (hoverStyle == StyleHit::NONE && UiPointInRect(m_invertedRect, fx, fy)) + if (hoverStyle == StyleHit::NONE && UiPointInRect(m_adjustRects[kAdjustInvert], fx, fy)) { hoverStyle = StyleHit::INVERT_TOGGLE; } @@ -2198,6 +2202,7 @@ namespace jdi m_pendingLaneMenu = false; m_pendingColorPicker = false; + m_pendingGainMenu = false; // 点击当前输入框以外的位置时提交输入。 if (m_editField != StyleEditField::NONE && @@ -2263,18 +2268,19 @@ namespace jdi { return BeginEdit(StyleEditField::OPACITY); } - // 系数增益:点击输入框即进入输入状态。 - if (UiPointInRect(m_gainFieldRect, fx, fy)) + // 「调整」行:系数调整(按下只登记,**抬起**时才展开二级面板, + // 与自定义颜色入口同一套交互)、取绝对值 / 中心反转(单击即切换)。 + if (UiPointInRect(m_adjustRects[kAdjustGain], fx, fy)) { - return BeginEdit(StyleEditField::GAIN); + m_pendingGainMenu = true; + return true; } - // 「取绝对值」/「中心反转」:单击即切换(作用顺序为 增益 → 绝对值 → 中心反转)。 - if (UiPointInRect(m_absoluteRect, fx, fy)) + if (UiPointInRect(m_adjustRects[kAdjustAbsolute], fx, fy)) { style->absolute = !style->absolute; return true; } - if (UiPointInRect(m_invertedRect, fx, fy)) + if (UiPointInRect(m_adjustRects[kAdjustInvert], fx, fy)) { style->inverted = !style->inverted; return true; @@ -2418,6 +2424,19 @@ namespace jdi } } + // 「系数调整」:按下时只登记,抬起时指针仍在同一按钮上才展开二级面板 + //(与自定义颜色入口一致:按住后移开再松开视为放弃)。 + if (m_pendingGainMenu) + { + m_pendingGainMenu = false; + const float fx = static_cast(x); + const float fy = static_cast(y); + if (UiPointInRect(m_adjustRects[kAdjustGain], fx, fy) && m_tab != nullptr) + { + changed = OpenGainMenu() || changed; + } + } + if (m_pendingLaneMenu) { m_pendingLaneMenu = false; @@ -2459,10 +2478,6 @@ namespace jdi { return BeginEdit(StyleEditField::OPACITY); } - if (UiPointInRect(m_gainFieldRect, fx, fy)) - { - return BeginEdit(StyleEditField::GAIN); - } RowPart part = RowPart::NONE; const int row = HitRow(x, y, part); @@ -2740,6 +2755,29 @@ namespace jdi return false; } + // 「系数调整」二级面板里的输入框提交:就地更新绘制系数,面板保持打开 + //(与网格 Y 轴上下限的菜单内联编辑一致),并把夹取后的真实值回写到输入框上。 + if (result.editCommit && result.id == CMD_GAIN_EDIT) + { + SignalStyle* style = StyleOf(SelectedRef()); + if (style != nullptr) + { + wchar_t* stop = nullptr; + const double parsed = std::wcstod(result.editText.c_str(), &stop); + // 非法/空输入保持原值(只把真实值回写到输入框,避免用户看到错误的数字)。 + const std::wstring trimmed = Trim(result.editText); + if (!trimmed.empty() && stop != trimmed.c_str() && std::isfinite(parsed)) + { + style->gain = static_cast(std::max(-kGainLimit, std::min(parsed, kGainLimit))); + } + if (m_menu != nullptr) + { + m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain)); + } + } + return false; + } + bool needRelayout = false; switch (result.id)