样式区「调整」行:系数调整(二级面板输入)+ 取绝对值 + 中心反转

- 移除上一轮新增的「系数增益」输入行与「取绝对值 / 中心反转」按钮行。
- 在「样式」下方、「线宽」上方新增「调整」行,一行三个等宽按钮:
  「系数调整」「取绝对值」「中心反转」(后两者即原按钮迁移至此)。
  样式区固定行数回到五行(颜色 / 样式 / 调整 / 线宽 / 透明度),标签列宽回到 44(只需容纳「调整」)。
- 「系数调整」交互与"自定义颜色色块入口"一致:按下只登记,**抬起**且指针仍在同一按钮上才展开
  二级面板;面板锚定在按钮下沿,内含一个数值输入框(默认 1,EditFilter::NUMBER)。
  提交后面板保持打开(与网格 Y 轴上下限的菜单内联编辑一致),并把夹取后的真实值回写输入框。
- 按钮启用态规则:绘制系数 != 1 时显示为"已启用"(与其它切换按钮启用样式完全一致:
  accentSoft 底 + accent 描边),等于默认值 1 时为"未启用"样式。
- 系数为 1 / 非保序变换的回退逻辑、SceneContentHash / 样式快照 / 折线缓存键等绘制链路不变。

验证(离屏 harness,SW_HIDE、不动鼠标键盘屏幕):
- [AL] 重构为:五行高度与行序(调整行在样式行与线宽行之间)、三按钮按序铺满且不重叠、
  三个标签都完整放得下(无裁切);
  「系数调整」按住不弹出 / 移开再松开取消 / 单击展开(SIGNAL_PANEL 范围、标题「系数调整」、
  初始值 1、锚定在按钮下沿)→ 点击输入框进入编辑 → 键入 2.5 回车提交(面板保持打开、
  输入框回显 2.5、折线缓存键携带新系数)→ 点别处关闭面板;
  像素级核对按钮样式:gain=1 时填充为 cardBg,gain=2.5 时填充与"取绝对值已启用"的按钮**逐像素同色**
  (0xFFCFE4F3)且与未启用态不同。
- 其余用例(样式变换顺序与顶点范围、拾色器两列等高、选区放大下 GX/图例命中、标题栏内存读数)
  保持不变。合计 549/549 通过;make clean && make all 零警告。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-13 13:51:31 +08:00
1 parent c3343907ec
commit 2f243dd557
2 files changed
+172 -123

No files matched your search

+20 -9
View File
@@ -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<gd::RectF, 3> m_modeRects; /**< 绘制样式按钮矩形。 */
// 样式区固定六行:颜色 / 样式 / 线宽 / 透明度 / 系数增益 / 取绝对值与中心反转。
// 样式区固定五行:颜色 / 样式 / 调整(系数调整·取绝对值·中心反转)/ 线宽 / 透明度。
// 各行控件共用同一内容列宽度与左边界。
std::array<gd::RectF, 6> m_styleRowRects; /**< 六行整体矩形(含标签列)。 */
std::array<gd::RectF, 5> 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<gd::RectF, 3> m_adjustRects;
std::vector<ListRow> 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 待确认的「关闭类」按钮(✕)。
+152 -114
View File
@@ -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<int>(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<float>(x);
const float fy = static_cast<float>(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<float>(kAdjustCount - 1)) /
static_cast<float>(kAdjustCount));
for (int index = 0; index < kAdjustCount; ++index)
{
m_adjustRects[static_cast<std::size_t>(index)] =
gd::RectF(contentLeft + static_cast<float>(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<std::size_t>(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<double>(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<double>(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<int>(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<int>(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<float>(std::max(-kGainLimit, std::min(parsed, kGainLimit)));
}
break;
}
case StyleEditField::SIGNAL_NAME:
{
if (row < 0 || row >= static_cast<int>(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<MenuItem> 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<int>(button.X),
static_cast<int>(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<float>(x);
const float fy = static_cast<float>(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<float>(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)