主题自适应配色 + 预览曲线连续配色 + 样式区复位按钮 + 均值滤波面板重做 + 面板缓存环带残留 + 调试层序 + 自由标记选取与性能
① 主题自适应(`Theme.cpp`):新增 `SrgbLuminance` / `ContrastRatio` / `EnsureContrast` /
`MixToLuminance` / `ThemeLaneBackground`;四套主题的结构色(gridWeak 1.30 / gridStrong 1.65 /
axisLine 2.10 / borderStrong 1.70)按底色自动校正,Solarised Light 逐位不变;网格叠底改用
`ThemeLaneBackground()`(4 处),深色主题下网格层与绘图区底色可辨。
② 派生预览曲线(`HighContrastDerived`)重写为**连续**方案:方向沿信号所在侧远离底色,
目标亮度 = max(与底色对比度 3.2, 信号亮度 ± 0.18),再按亮度平滑混色;调用点传入真实网格底色
(旧实现默认写死 Solarised Light 底色 ⇒ 深色主题永远选到更暗的那一侧、几乎看不见)。
③ 信号样式区「线宽 / 透明度」两行右侧新增**复位按钮**(`UiPaintResetIcon` 成为唯一实现,
菜单/弱峰面板/样式区共用同一套笔画);默认线宽统一到 `DefaultSignalLineWidth(finiteCount)`
(单点信号取 10.0)。按钮右边界与内容列对齐,内容列过窄时整列不显示。
④ 「均值滤波」面板重做:信号色叠底标题栏 + 复选框 + 展开三角 + 「均值点数 / 迭代次数」两行滑块
(点击数值可改滑块上限、右侧复位)+ 内容区滚动;新增 `MeanFilterItem::{maxPoints,maxIterations,
collapsed}` 与 `Lane::meanFilterScroll`,缓存键同步补全。
⑤ 浮动面板命中像素缓存时只贴**面板圆角形状之内**的像素(`CreateRoundRectRgn` 裁剪)并按本帧重画投影
⇒ 中键移视界后面板周围不再出现一圈旧波形。
⑥ 显示布局边界:每个浮动面板**独占一个绘制层**(按 `m_panelOrder` 递增),绘制侧
`UiSetTextLayer` 在同一循环内同步推进 ⇒ 下层面板的边框/文字不再画到上层面板上。
⑦ 自由标记:采样候选距离改为**二维欧氏距离**(单点信号不再在整幅宽度内吸住光标),
纵向光标画在鼠标位置、圆点落在真实采样点(新增 `FreeMarkerProbe::pointX`);
`MinorCellSparse` 只对胜出信号算一次;折线缓存容量改为 `max(64, 信号数×2)` 且查找改为
单键哈希 ⇒ MF4 的 285 信号合并网格中 `ProbeFreeMarker` 平均 0.11ms。
验证:`.preflight` 探针重建(`panelcheck` 71/71、`markercheck` 13/13、`ringcheck` 6/6,合计 90 条,
LTO 目标文件上复跑同样全绿);`make release` / `make debug` 零警告;产物 815,104 B。
文档:`agent_mem.md` 第十六轮(含补丁脚本与编码/行尾踩坑)。
This commit is contained in:
1 parent
71195c4c48
commit
bdd703b88b
24 files changed
+1815
-256
No files matched your search
@@ -389,9 +389,11 @@ namespace jdi
|
||||
m_editTarget == PlotEditTarget::EDGE_CENTER_ITERATIONS ||
|
||||
m_editTarget == PlotEditTarget::MEAN_FILTER_POINTS ||
|
||||
m_editTarget == PlotEditTarget::MEAN_FILTER_ITERATIONS ||
|
||||
m_editTarget == PlotEditTarget::PEAK_ITERATIONS_MAX)
|
||||
m_editTarget == PlotEditTarget::PEAK_ITERATIONS_MAX ||
|
||||
m_editTarget == PlotEditTarget::MEAN_POINTS_MAX ||
|
||||
m_editTarget == PlotEditTarget::MEAN_ITERATIONS_MAX)
|
||||
{
|
||||
// 「均值点数 X」/「迭代次数 N」(均值滤波、中心轴)与「迭代次数 N 上限」(弱峰调整):
|
||||
// 「均值点数 X」/「迭代次数 N」(均值滤波、中心轴)与「滑块上限」(弱峰调整 / 均值滤波):
|
||||
// 输入框都落在对应的字段/滑块行矩形上。
|
||||
if (m_editFieldRect.Width <= 0.0f)
|
||||
{
|
||||
@@ -641,6 +643,44 @@ namespace jdi
|
||||
return true;
|
||||
}
|
||||
|
||||
// 「均值滤波」两行滑块的**上限**:与「弱峰调整 → 迭代次数上限」同一交互语义
|
||||
//(点击滑块右侧数值 → 整行变成输入框,输入正整数作为新的量程上限)。
|
||||
// 规则:空内容 / 非法值(低于下限或超出允许范围)→ 保持原上限不变;
|
||||
// 生效后若当前值超过新上限,自动夹到新上限。
|
||||
if (target == PlotEditTarget::MEAN_POINTS_MAX ||
|
||||
target == PlotEditTarget::MEAN_ITERATIONS_MAX)
|
||||
{
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
if (lane.FindMeanFilterItem(m_editMeanRef) == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsedOk)
|
||||
{
|
||||
const bool isPoints = (target == PlotEditTarget::MEAN_POINTS_MAX);
|
||||
const int minimum = isPoints ? kMeanFilterPointsMin : kMeanFilterIterationsMin;
|
||||
const int limit = isPoints ? kMeanFilterPointsMaxLimit : kMeanFilterIterationsMaxLimit;
|
||||
const long long value = static_cast<long long>(std::llround(parsed));
|
||||
if (value >= minimum && value <= limit)
|
||||
{
|
||||
Lane::MeanFilterItem& item = lane.EnsureMeanFilterItem(m_editMeanRef);
|
||||
if (isPoints)
|
||||
{
|
||||
item.maxPoints = static_cast<int>(value);
|
||||
item.points = std::max(minimum, std::min(item.maxPoints, item.points));
|
||||
}
|
||||
else
|
||||
{
|
||||
item.maxIterations = static_cast<int>(value);
|
||||
item.iterations =
|
||||
std::max(minimum, std::min(item.maxIterations, item.iterations));
|
||||
}
|
||||
m_meanCurveCache.clear();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 「均值点数 X」/「迭代次数 N」:作用于被勾选信号的均值滤波曲线,夹在合理范围内。
|
||||
// 「中心轴边沿计数」用**完全相同的参数与语义**,只是作用于自己的条目列表。
|
||||
const bool centerTarget =
|
||||
@@ -882,7 +922,9 @@ namespace jdi
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
case PlotDragMode::PEAK_LEVEL:
|
||||
case PlotDragMode::PEAK_ITERATIONS:
|
||||
// 「弱峰调整」滑块拖拽 → 左右箭头。
|
||||
case PlotDragMode::MEAN_POINTS:
|
||||
case PlotDragMode::MEAN_ITERATIONS:
|
||||
// 「弱峰调整 / 均值滤波」参数滑块拖拽 → 左右箭头。
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
case PlotDragMode::MOVE_SIGNAL:
|
||||
// 拖动信号(参考 Photoshop 平移元素的抓握光标)。
|
||||
@@ -1393,6 +1435,72 @@ namespace jdi
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case PlotDragMode::MEAN_POINTS:
|
||||
case PlotDragMode::MEAN_ITERATIONS:
|
||||
{
|
||||
// 「均值滤波」某信号的参数滑块:量程 = 下限 … 该信号自己的上限。
|
||||
if (m_tab == nullptr || m_meanDragLane < 0 ||
|
||||
m_meanDragLane >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(m_meanDragLane)];
|
||||
if (lane.FindMeanFilterItem(m_meanDragRef) == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Lane::MeanFilterItem& item = lane.EnsureMeanFilterItem(m_meanDragRef);
|
||||
const bool pointsMode = (m_dragMode == PlotDragMode::MEAN_POINTS);
|
||||
const EdgePanelLayout layout =
|
||||
EdgePanelLayoutFor(m_meanDragLane, EdgePanelKind::MEAN);
|
||||
int rowIndex = -1;
|
||||
for (std::size_t candidate = 0; candidate < layout.rowRefs.size(); ++candidate)
|
||||
{
|
||||
if (layout.rowRefs[candidate] == m_meanDragRef)
|
||||
{
|
||||
rowIndex = static_cast<int>(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rowIndex < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const std::vector<gd::RectF>& tracks =
|
||||
pointsMode ? layout.rowLevelTracks : layout.rowIterTracks;
|
||||
if (static_cast<std::size_t>(rowIndex) >= tracks.size() ||
|
||||
tracks[static_cast<std::size_t>(rowIndex)].Width <= 0.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const double ratio =
|
||||
UiSliderRatioFromX(tracks[static_cast<std::size_t>(rowIndex)], fx);
|
||||
if (pointsMode)
|
||||
{
|
||||
const int maximum = std::max(
|
||||
kMeanFilterPointsMin, std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
item.points = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterPointsMin +
|
||||
static_cast<int>(std::lround(ratio * (maximum - kMeanFilterPointsMin)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
const int maximum =
|
||||
std::max(kMeanFilterIterationsMin,
|
||||
std::min(kMeanFilterIterationsMaxLimit, item.maxIterations));
|
||||
item.iterations = std::max(
|
||||
kMeanFilterIterationsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterIterationsMin +
|
||||
static_cast<int>(std::lround(
|
||||
ratio * (maximum - kMeanFilterIterationsMin)))));
|
||||
}
|
||||
m_meanCurveCache.clear();
|
||||
m_dragMoved = true;
|
||||
return true;
|
||||
}
|
||||
case PlotDragMode::LANE_REORDER:
|
||||
{
|
||||
// 被拖拽的网格自由跟随鼠标;目标位置变化时其它网格实时让位(吸附)。
|
||||
@@ -1526,6 +1634,35 @@ namespace jdi
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case PlotDragMode::MEAN_SCROLL:
|
||||
{
|
||||
// 「均值滤波」面板内容滚动条:按滑块在轨道里的位置换算滚动偏移。
|
||||
if (m_tab == nullptr || m_markerScrollDragLane < 0 ||
|
||||
m_markerScrollDragLane >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
const EdgePanelLayout layout =
|
||||
EdgePanelLayoutFor(m_markerScrollDragLane, EdgePanelKind::MEAN);
|
||||
const float travel = layout.scrollTrack.Height - layout.scrollThumb.Height;
|
||||
const float maxScroll =
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
if (layout.scrollTrack.Width <= 0.0f || travel <= 0.0f || maxScroll <= 0.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const float thumbTop =
|
||||
std::max(layout.scrollTrack.Y,
|
||||
std::min(static_cast<float>(y) - m_markerScrollGrabOffset,
|
||||
layout.scrollTrack.Y + travel));
|
||||
const float ratio = (thumbTop - layout.scrollTrack.Y) / travel;
|
||||
if (SetLaneMeanFilterScroll(m_markerScrollDragLane, ratio * maxScroll))
|
||||
{
|
||||
m_dragMoved = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case PlotDragMode::NONE:
|
||||
default:
|
||||
break;
|
||||
@@ -2054,6 +2191,161 @@ namespace jdi
|
||||
{
|
||||
const bool meanMode = (kind == EdgePanelKind::MEAN);
|
||||
const bool boostMode = (kind == EdgePanelKind::PEAK);
|
||||
// 「均值滤波」:组头三角(展开 / 收起参数行)+ 两行参数滑块
|
||||
//(轨道拖动 / 点击数值改上限 / 右侧复位按钮)。
|
||||
// ⚠️ 所有命中都要求落在**内容区**之内:滚动到可视范围之外的行仍保留矩形,
|
||||
// 若不加这条限制,它们会抢走底部「构建为新信号」按钮的点击。
|
||||
if (meanMode)
|
||||
{
|
||||
Lane& meanLane = m_tab->Lanes()[static_cast<std::size_t>(index)];
|
||||
// 内容区右侧的滚动条滑块:按下即进入拖动(与「自由标记」面板同一交互)。
|
||||
if (layout.scrollThumb.Width > 0.0f &&
|
||||
UiPointInRect(layout.scrollThumb, fx, fy))
|
||||
{
|
||||
m_dragMode = PlotDragMode::MEAN_SCROLL;
|
||||
m_markerScrollDragLane = index;
|
||||
m_markerScrollGrabOffset = fy - layout.scrollThumb.Y;
|
||||
return true;
|
||||
}
|
||||
const bool insideContent =
|
||||
UiPointInRect(layout.content, fx, fy);
|
||||
for (std::size_t rowIndex = 0; rowIndex < layout.rowRefs.size(); ++rowIndex)
|
||||
{
|
||||
const SignalRef& ref = layout.rowRefs[rowIndex];
|
||||
if (!insideContent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// 只允许命中**完全落在内容区里**的行(部分滚出的行其矩形会与内容区
|
||||
// 边界重叠,放行会造出"看得见一半却能点"的幽灵热区)。
|
||||
if (rowIndex < layout.rowHeaders.size())
|
||||
{
|
||||
const gd::RectF& headerRect = layout.rowHeaders[rowIndex];
|
||||
if (headerRect.Y < layout.content.Y - 0.5f ||
|
||||
headerRect.Y + headerRect.Height >
|
||||
layout.content.Y + layout.content.Height + 0.5f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const bool hasParams =
|
||||
rowIndex < layout.rowLevelTracks.size() &&
|
||||
layout.rowLevelTracks[rowIndex].Width > 0.0f &&
|
||||
meanLane.FindMeanFilterItem(ref) != nullptr;
|
||||
if (hasParams)
|
||||
{
|
||||
Lane::MeanFilterItem& item = meanLane.EnsureMeanFilterItem(ref);
|
||||
// 复位:均值点数 → 默认值(同时把滑块上限恢复为默认上限)。
|
||||
if (rowIndex < layout.rowLevelResets.size() &&
|
||||
layout.rowLevelResets[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowLevelResets[rowIndex], fx, fy))
|
||||
{
|
||||
item.points = kMeanFilterPointsDefault;
|
||||
item.maxPoints = kMeanFilterPointsDefaultMax;
|
||||
m_meanCurveCache.clear();
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
// 复位:迭代次数 → 1(上限恢复为默认上限)。
|
||||
if (rowIndex < layout.rowIterResets.size() &&
|
||||
layout.rowIterResets[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowIterResets[rowIndex], fx, fy))
|
||||
{
|
||||
item.iterations = kMeanFilterIterationsMin;
|
||||
item.maxIterations = kMeanFilterIterationsDefaultMax;
|
||||
m_meanCurveCache.clear();
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
// 点击「均值点数」右侧数值 → 输入滑块上限。
|
||||
if (rowIndex < layout.rowLevelValues.size() &&
|
||||
layout.rowLevelValues[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowLevelValues[rowIndex], fx, fy))
|
||||
{
|
||||
m_editMeanRef = ref;
|
||||
m_meanEditCurrentMax = item.maxPoints;
|
||||
const gd::RectF& track = layout.rowLevelTracks[rowIndex];
|
||||
const gd::RectF& value = layout.rowLevelValues[rowIndex];
|
||||
const gd::RectF rowRect(track.X, value.Y,
|
||||
value.X + value.Width - track.X,
|
||||
value.Height);
|
||||
BeginEdit(PlotEditTarget::MEAN_POINTS_MAX, index, L"", rowRect);
|
||||
m_edit.Begin(L"", false, EditFilter::BYTE, 0u);
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
// 点击「迭代次数」右侧数值 → 输入滑块上限。
|
||||
if (rowIndex < layout.rowIterValues.size() &&
|
||||
layout.rowIterValues[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowIterValues[rowIndex], fx, fy))
|
||||
{
|
||||
m_editMeanRef = ref;
|
||||
m_meanEditCurrentMax = item.maxIterations;
|
||||
const gd::RectF& track = layout.rowIterTracks[rowIndex];
|
||||
const gd::RectF& value = layout.rowIterValues[rowIndex];
|
||||
const gd::RectF rowRect(track.X, value.Y,
|
||||
value.X + value.Width - track.X,
|
||||
value.Height);
|
||||
BeginEdit(PlotEditTarget::MEAN_ITERATIONS_MAX, index, L"", rowRect);
|
||||
m_edit.Begin(L"", false, EditFilter::BYTE, 0u);
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
// 拖动「均值点数」滑块。
|
||||
if (rowIndex < layout.rowLevelTracks.size() &&
|
||||
WeakPeakSliderHit(layout.rowLevelTracks[rowIndex], m_scale, fx, fy))
|
||||
{
|
||||
const int maximum = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
m_dragMode = PlotDragMode::MEAN_POINTS;
|
||||
m_meanDragLane = index;
|
||||
m_meanDragRef = ref;
|
||||
const double ratio = UiSliderRatioFromX(layout.rowLevelTracks[rowIndex], fx);
|
||||
item.points = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterPointsMin +
|
||||
static_cast<int>(std::lround(
|
||||
ratio *
|
||||
(maximum - kMeanFilterPointsMin)))));
|
||||
m_meanCurveCache.clear();
|
||||
return true;
|
||||
}
|
||||
// 拖动「迭代次数」滑块。
|
||||
if (rowIndex < layout.rowIterTracks.size() &&
|
||||
WeakPeakSliderHit(layout.rowIterTracks[rowIndex], m_scale, fx, fy))
|
||||
{
|
||||
const int maximum = std::max(
|
||||
kMeanFilterIterationsMin,
|
||||
std::min(kMeanFilterIterationsMaxLimit, item.maxIterations));
|
||||
m_dragMode = PlotDragMode::MEAN_ITERATIONS;
|
||||
m_meanDragLane = index;
|
||||
m_meanDragRef = ref;
|
||||
const double ratio = UiSliderRatioFromX(layout.rowIterTracks[rowIndex], fx);
|
||||
item.iterations = std::max(
|
||||
kMeanFilterIterationsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterIterationsMin +
|
||||
static_cast<int>(std::lround(
|
||||
ratio *
|
||||
(maximum - kMeanFilterIterationsMin)))));
|
||||
m_meanCurveCache.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 组头左侧三角:切换参数行的收起 / 展开(只有勾选后才有三角)。
|
||||
if (rowIndex < layout.rowTriangles.size() &&
|
||||
layout.rowTriangles[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowTriangles[rowIndex], fx, fy))
|
||||
{
|
||||
Lane::MeanFilterItem& item = meanLane.EnsureMeanFilterItem(ref);
|
||||
item.collapsed = !item.collapsed;
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Lane& tabLane = m_tab->Lanes()[static_cast<std::size_t>(index)];
|
||||
|
||||
// 「弱峰调整」:先判两行滑块(数值文本 → 改上限;轨道 → 拖动)。
|
||||
@@ -2707,6 +2999,9 @@ namespace jdi
|
||||
// 弱峰滑块拖拽结束:清掉目标(避免留下上一轮的信号引用)。
|
||||
m_peakDragLane = -1;
|
||||
m_peakDragRef = SignalRef();
|
||||
// 「均值滤波」参数滑块拖拽结束:同样清掉目标。
|
||||
m_meanDragLane = -1;
|
||||
m_meanDragRef = SignalRef();
|
||||
// 「标记透明度」滑块 / 面板内容滚动条拖拽结束。
|
||||
m_markerOpacityDragLane = -1;
|
||||
m_markerScrollDragLane = -1;
|
||||
@@ -2755,24 +3050,49 @@ namespace jdi
|
||||
int panelLane = -1;
|
||||
int panelKind = 0;
|
||||
if (TopPanelAtPoint(static_cast<float>(m_mouseX), static_cast<float>(m_mouseY),
|
||||
panelLane, panelKind) &&
|
||||
static_cast<EdgePanelKind>(panelKind) == EdgePanelKind::MARK)
|
||||
panelLane, panelKind))
|
||||
{
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, EdgePanelKind::MARK);
|
||||
if (layout.content.Width > 0.0f &&
|
||||
UiPointInRect(layout.content, static_cast<float>(m_mouseX),
|
||||
static_cast<float>(m_mouseY)))
|
||||
const EdgePanelKind hoveredKind = static_cast<EdgePanelKind>(panelKind);
|
||||
// 「均值滤波」面板:内容区超高时用滚轮翻页(上百个信号合并时必备)。
|
||||
if (hoveredKind == EdgePanelKind::MEAN)
|
||||
{
|
||||
const float maxScroll =
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
if (maxScroll > 0.0f)
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, EdgePanelKind::MEAN);
|
||||
if (layout.content.Width > 0.0f &&
|
||||
UiPointInRect(layout.content, static_cast<float>(m_mouseX),
|
||||
static_cast<float>(m_mouseY)))
|
||||
{
|
||||
const float next = static_cast<float>(Tab().Lanes()[static_cast<std::size_t>(
|
||||
panelLane)]
|
||||
.freeMarkerScroll -
|
||||
notches * 40.0 * m_scale);
|
||||
SetLaneFreeMarkerScroll(panelLane, std::max(0.0f, std::min(next, maxScroll)));
|
||||
return true;
|
||||
const float maxScroll =
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
if (maxScroll > 0.0f)
|
||||
{
|
||||
const float current =
|
||||
Tab().Lanes()[static_cast<std::size_t>(panelLane)].meanFilterScroll;
|
||||
const float next = current - static_cast<float>(notches * 40.0 * m_scale);
|
||||
SetLaneMeanFilterScroll(panelLane,
|
||||
std::max(0.0f, std::min(next, maxScroll)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hoveredKind == EdgePanelKind::MARK)
|
||||
{
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, EdgePanelKind::MARK);
|
||||
if (layout.content.Width > 0.0f &&
|
||||
UiPointInRect(layout.content, static_cast<float>(m_mouseX),
|
||||
static_cast<float>(m_mouseY)))
|
||||
{
|
||||
const float maxScroll =
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
if (maxScroll > 0.0f)
|
||||
{
|
||||
const float next = static_cast<float>(Tab().Lanes()[static_cast<std::size_t>(
|
||||
panelLane)]
|
||||
.freeMarkerScroll -
|
||||
notches * 40.0 * m_scale);
|
||||
SetLaneFreeMarkerScroll(panelLane,
|
||||
std::max(0.0f, std::min(next, maxScroll)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user