中心轴曲线配色 + 筛选框交互完善 + 双光标拖拽性能修复
- 中心轴边沿计数的平均值曲线改为「由信号色派生的高对比度颜色」:保持色相、 大幅拉开明度(与原始波形高对比),透明度与原信号一致;原信号改色时曲线 颜色随之变化,而光标显示该曲线数值时仍沿用原始信号的配色样式。 - 信号筛选框:内容区左右内缩改为与上下一致(不再「左右空隙明显更大」); 有输入时右侧显示清空 ✕(悬停高亮 + 手型光标,点击清空且保持编辑态); 筛选无结果时在列表区提示「无匹配的筛选信号」。 - 光标数值超出上下视界时,三角箭头标识下移 kCursorHandleReserve, 不再遮挡光标自身的三角把手(含双光标模式)。 - 性能:新增「装饰区域脏标记 + 绘图区局部回送」快路径——绘图区内的悬停/拖拽 期间跳过标签栏/工具栏/面板/分隔条的重绘,且只 BitBlt 绘图区矩形; WM_PAINT 依据更新区域是否落在绘图区内决定快慢路径,窗口缩放/遮挡露出/ 菜单与拾色器弹出等场景仍整帧重绘。 以 VERY_BIG_DATA.CSV 全信号双光标拖拽实测:单帧耗时由约 28ms 降至约 12ms。 同时修复拖拽期间残留的信号悬停提示气泡。
This commit is contained in:
1 parent
fe72afc83c
commit
8ae8d7af4f
6 files changed
+333
-45
No files matched your search
@@ -293,6 +293,14 @@ namespace jdi
|
||||
*/
|
||||
bool HitTest(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief 查询该位置应使用的鼠标光标(当前仅「筛选信号」清空按钮 ✕ 使用手型)。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
* @param [IN] y 客户区纵坐标。
|
||||
* @return 需要特殊光标时返回光标句柄,否则返回 nullptr。
|
||||
*/
|
||||
HCURSOR QueryCursor(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief 将列表滚动到指定信号所在位置。
|
||||
* @param [IN] ref 目标信号引用。
|
||||
@@ -347,7 +355,8 @@ namespace jdi
|
||||
OPACITY_VALUE, /**< 不透明度数值框(可点击输入)。 */
|
||||
WIDTH_SLIDER, /**< 线宽滑块。 */
|
||||
WIDTH_VALUE, /**< 线宽数值框(可点击输入)。 */
|
||||
TOGGLE_ALL /**< 全部显示/隐藏按钮。 */
|
||||
TOGGLE_ALL, /**< 全部显示/隐藏按钮。 */
|
||||
FILTER_CLEAR /**< 「筛选信号」输入框右侧的清空 ✕。 */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -530,6 +539,17 @@ namespace jdi
|
||||
*/
|
||||
gd::RectF FilterFieldRect() const;
|
||||
|
||||
/**
|
||||
* @brief 计算「筛选信号」输入框右侧清空按钮 ✕ 的矩形(无输入时为空矩形)。
|
||||
* @return 清空按钮矩形。
|
||||
*/
|
||||
gd::RectF FilterClearRect() const;
|
||||
|
||||
/**
|
||||
* @brief 清空筛选内容(点击 ✕ 时调用),并刷新列表。
|
||||
*/
|
||||
void ClearFilter();
|
||||
|
||||
/**
|
||||
* @brief 计算指定行中名称区域的矩形(列表行内联编辑使用)。
|
||||
* @param [IN] row 行下标。
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace jdi
|
||||
const float kGroupSeparatorWidth = 1.0f; /**< 分组标签右侧分隔线宽度。 */
|
||||
const float kFilterHeight = 26.0f; /**< 「筛选信号」输入框高度。 */
|
||||
const float kFilterPadV = 5.0f; /**< 筛选框上下留白。 */
|
||||
const float kFilterTextPad = 4.0f; /**< 筛选框内文字/输入框相对外框的内缩(四边一致)。 */
|
||||
|
||||
/**
|
||||
* @brief 大小写无关的通配符匹配(`*` 匹配任意长度的任意字符序列)。
|
||||
@@ -218,6 +219,36 @@ namespace jdi
|
||||
return UiPointInRect(m_filterRect, static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
void SignalPanel::ClearFilter()
|
||||
{
|
||||
if (m_filterText.empty() && !m_filterEditing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_filterText.clear();
|
||||
if (m_filterEditing)
|
||||
{
|
||||
// 编辑态下一并重置输入模型,避免残留旧文本(Begin 会整段选中/清空)。
|
||||
m_filterEdit.Begin(std::wstring(), false, EditFilter::ANY, 0u);
|
||||
}
|
||||
// 清空后回到完整列表:重建行集合、回到顶部。
|
||||
m_rowsDirty = true;
|
||||
m_scrollOffset = 0.0f;
|
||||
m_layoutDirty = true;
|
||||
}
|
||||
|
||||
HCURSOR SignalPanel::QueryCursor(int x, int y) const
|
||||
{
|
||||
// 清空按钮 ✕ 是独立可点击控件,用手型光标给出除高亮之外的额外交互提示。
|
||||
const gd::RectF clearRect = FilterClearRect();
|
||||
if (clearRect.Width > 0.0f &&
|
||||
UiPointInRect(clearRect, static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SignalPanel::BeginFilterEdit()
|
||||
{
|
||||
if (m_filterEditing)
|
||||
@@ -723,25 +754,47 @@ namespace jdi
|
||||
UiDrawRoundRect(g, m_filterRect, kCorner * m_scale,
|
||||
m_filterEditing ? m_theme->accent : m_theme->border,
|
||||
m_filterEditing ? 1.6f : 1.0f);
|
||||
|
||||
const float pad = kFilterTextPad * m_scale;
|
||||
const gd::RectF clearRect = FilterClearRect();
|
||||
// 有输入内容时在右侧留出 ✕ 的位置,文本不会压到按钮上。
|
||||
const float textRight = (clearRect.Width > 0.0f) ? clearRect.X : (m_filterRect.X + m_filterRect.Width);
|
||||
const float textWidth = std::max(8.0f, textRight - pad - (m_filterRect.X + pad));
|
||||
const gd::RectF textRect(m_filterRect.X + pad, m_filterRect.Y, textWidth,
|
||||
m_filterRect.Height);
|
||||
|
||||
if (m_filterEditing)
|
||||
{
|
||||
UiDrawEditField(g, FilterFieldRect(), m_filterEdit, m_fonts->uiSmall, m_scale);
|
||||
gd::RectF fieldRect = FilterFieldRect();
|
||||
fieldRect.Width = std::max(8.0f, textWidth + pad);
|
||||
UiDrawEditField(g, fieldRect, m_filterEdit, m_fonts->uiSmall, m_scale);
|
||||
}
|
||||
else if (m_filterText.empty())
|
||||
{
|
||||
UiText(g, L"筛选信号", m_fonts->uiSmall,
|
||||
gd::RectF(m_filterRect.X + 8.0f * m_scale, m_filterRect.Y,
|
||||
m_filterRect.Width - 16.0f * m_scale, m_filterRect.Height),
|
||||
m_theme->textMuted, TextAlign::LEFT);
|
||||
UiText(g, L"筛选信号", m_fonts->uiSmall, textRect, m_theme->textMuted, TextAlign::LEFT);
|
||||
}
|
||||
else
|
||||
{
|
||||
UiText(g, UiEllipsize(g, m_filterText, m_fonts->uiSmall,
|
||||
m_filterRect.Width - 16.0f * m_scale),
|
||||
m_fonts->uiSmall,
|
||||
gd::RectF(m_filterRect.X + 8.0f * m_scale, m_filterRect.Y,
|
||||
m_filterRect.Width - 16.0f * m_scale, m_filterRect.Height),
|
||||
m_theme->text, TextAlign::LEFT);
|
||||
UiText(g, UiEllipsize(g, m_filterText, m_fonts->uiSmall, textWidth), m_fonts->uiSmall,
|
||||
textRect, m_theme->text, TextAlign::LEFT);
|
||||
}
|
||||
|
||||
// 清空按钮 ✕:仅在筛选框有内容时出现,悬停高亮(鼠标光标保持箭头)。
|
||||
if (clearRect.Width > 0.0f)
|
||||
{
|
||||
const bool clearHot = (m_hoverStyle == StyleHit::FILTER_CLEAR);
|
||||
if (clearHot)
|
||||
{
|
||||
UiFillRoundRect(g, clearRect, 3.0f * m_scale, WithAlpha(m_theme->danger, 0x33));
|
||||
}
|
||||
const std::uint32_t crossColor = clearHot ? m_theme->danger : m_theme->textMuted;
|
||||
const float crossPad = clearRect.Width * 0.28f;
|
||||
UiDrawLine(g, clearRect.X + crossPad, clearRect.Y + crossPad,
|
||||
clearRect.X + clearRect.Width - crossPad,
|
||||
clearRect.Y + clearRect.Height - crossPad, crossColor, 1.5f * m_scale);
|
||||
UiDrawLine(g, clearRect.X + clearRect.Width - crossPad, clearRect.Y + crossPad,
|
||||
clearRect.X + crossPad, clearRect.Y + clearRect.Height - crossPad, crossColor,
|
||||
1.5f * m_scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -755,6 +808,12 @@ namespace jdi
|
||||
UiText(g, L"尚未加载数据,请打开 CSV 文件", m_fonts->uiSmall, m_listRect, m_theme->textMuted,
|
||||
TextAlign::CENTER);
|
||||
}
|
||||
else if (m_rows.empty() && !m_filterText.empty())
|
||||
{
|
||||
// 筛选结果为空:明确提示"无匹配",而不是让人误以为数据丢失。
|
||||
UiText(g, L"无匹配的筛选信号", m_fonts->uiSmall, m_listRect, m_theme->textMuted,
|
||||
TextAlign::CENTER);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int rowCount = static_cast<int>(m_rows.size());
|
||||
@@ -1393,11 +1452,23 @@ namespace jdi
|
||||
|
||||
gd::RectF SignalPanel::FilterFieldRect() const
|
||||
{
|
||||
// 输入框内容区:与占位提示/显示文本使用同一内缩,保证编辑时不跳动。
|
||||
const float inset = 7.0f * m_scale;
|
||||
return gd::RectF(m_filterRect.X + inset, m_filterRect.Y + 2.0f * m_scale,
|
||||
// 输入框内容区:左右与上下使用**同一内缩**,避免编辑态出现"左右空隙明显更大"的观感;
|
||||
// 占位提示与显示文本也用同一内缩,因此进入/退出编辑时文字不会跳动。
|
||||
const float inset = kFilterTextPad * m_scale;
|
||||
return gd::RectF(m_filterRect.X + inset, m_filterRect.Y + inset,
|
||||
std::max(8.0f, m_filterRect.Width - inset * 2.0f),
|
||||
std::max(8.0f, m_filterRect.Height - 4.0f * m_scale));
|
||||
std::max(8.0f, m_filterRect.Height - inset * 2.0f));
|
||||
}
|
||||
|
||||
gd::RectF SignalPanel::FilterClearRect() const
|
||||
{
|
||||
if (m_filterText.empty())
|
||||
{
|
||||
return gd::RectF();
|
||||
}
|
||||
const float size = std::max(12.0f, m_filterRect.Height - kFilterTextPad * 2.0f * m_scale);
|
||||
const float right = m_filterRect.X + m_filterRect.Width - kFilterTextPad * m_scale;
|
||||
return gd::RectF(right - size, m_filterRect.Y + (m_filterRect.Height - size) * 0.5f, size, size);
|
||||
}
|
||||
|
||||
gd::RectF SignalPanel::EditFieldRect() const
|
||||
@@ -1610,6 +1681,15 @@ namespace jdi
|
||||
{
|
||||
if (m_filterEditing)
|
||||
{
|
||||
// 编辑态下 ✕ 位于输入框内容区之内,必须优先于插入符定位处理,
|
||||
// 否则点击会被当成"把光标移到此处"而清空不了内容。
|
||||
const gd::RectF clearRect = FilterClearRect();
|
||||
if (clearRect.Width > 0.0f &&
|
||||
UiPointInRect(clearRect, static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
ClearFilter();
|
||||
return true;
|
||||
}
|
||||
return UiEditFieldMouseDown(m_fonts->uiSmall, FilterFieldRect(), m_filterEdit, m_scale, x, y);
|
||||
}
|
||||
if (m_editField == StyleEditField::NONE)
|
||||
@@ -1786,6 +1866,11 @@ namespace jdi
|
||||
{
|
||||
hoverStyle = StyleHit::TOGGLE_ALL;
|
||||
}
|
||||
else if (UiPointInRect(FilterClearRect(), fx, fy))
|
||||
{
|
||||
// 筛选框右侧的清空 ✕(仅在筛选框有内容时存在)。
|
||||
hoverStyle = StyleHit::FILTER_CLEAR;
|
||||
}
|
||||
else if (m_tab != nullptr && StyleOf(SelectedRef()) != nullptr)
|
||||
{
|
||||
for (int index = 0; index < m_colorCount; ++index)
|
||||
@@ -1859,6 +1944,12 @@ namespace jdi
|
||||
// 「筛选信号」输入框:单击进入编辑;点击其它位置则先结束筛选编辑(文本已实时生效)。
|
||||
if (IsOnFilterBox(x, y))
|
||||
{
|
||||
// 右侧 ✕:清空筛选内容(不退出编辑态,便于继续输入)。
|
||||
if (UiPointInRect(FilterClearRect(), fx, fy))
|
||||
{
|
||||
ClearFilter();
|
||||
return true;
|
||||
}
|
||||
BeginFilterEdit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user