Skip to content

Commit a993ee6

Browse files
committed
Fix settings slider knob unclickable at track minimum (#242)
The interact rect started at trackLeft, which is also the min-value knob center, so the left half of the disc missed hit-testing. Expand the hit pad by the 8px knob radius.
1 parent c6dbbff commit a993ee6

3 files changed

Lines changed: 36 additions & 3 deletions

File tree

‎QuickView/SettingsOverlay.cpp‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3644,9 +3644,10 @@ void SettingsOverlay::Render(ID2D1DeviceContext* pRT, float winW, float winH) {
36443644
const float valueW = 80.0f * s;
36453645
const float buttonW = 28.0f * s;
36463646

3647-
// Standard slider rect (Track + Padding)
3647+
// Track + right pad + knob radius so a min-value knob is still hittable.
3648+
const float knobPad = QuickView::SliderKnobHitRadius();
36483649
item.interactRect = D2D1::RectF(
3649-
controlRect.right - (trackW + padding), item.rect.top,
3650+
controlRect.right - (trackW + padding) - knobPad, item.rect.top,
36503651
controlRect.right, item.rect.bottom);
36513652

36523653
if (item.onReset) {
@@ -4411,6 +4412,19 @@ SettingsAction SettingsOverlay::OnMouseMove(float x, float y) {
44114412
}
44124413
}
44134414

4415+
// Knob at min sits half outside the track; test it even if interactRect is stale.
4416+
if (item.type == OptionType::Slider && !item.isDisabled) {
4417+
const float val = item.pFloatVal ? *item.pFloatVal : item.minVal;
4418+
const QuickView::SliderGeom g = QuickView::ComputeSliderGeom(
4419+
item.rect.right, item.rect.top, item.rect.bottom,
4420+
m_uiScale, val, item.minVal, item.maxVal);
4421+
if (QuickView::HitTestSlider(g, x, y, item.rect.top, item.rect.bottom, item.rect.right)) {
4422+
m_pHoverItem = &item;
4423+
item.isHovered = true;
4424+
g_currentCursor = ::LoadCursor(NULL, IDC_HAND);
4425+
}
4426+
}
4427+
44144428
bool inR1 = (x >= item.interactRect.left && x <= item.interactRect.right &&
44154429
y >= item.interactRect.top && y <= item.interactRect.bottom);
44164430
bool inR2 = (x >= item.interactRect2.left && x <= item.interactRect2.right &&
@@ -4429,7 +4443,7 @@ SettingsAction SettingsOverlay::OnMouseMove(float x, float y) {
44294443
const QuickView::SliderGeom g = QuickView::ComputeSliderGeom(
44304444
item.rect.right, item.rect.top, item.rect.bottom,
44314445
m_uiScale, val, item.minVal, item.maxVal);
4432-
if (x >= g.trackLeft && x <= item.rect.right && y >= item.rect.top && y <= item.rect.bottom) {
4446+
if (QuickView::HitTestSlider(g, x, y, item.rect.top, item.rect.bottom, item.rect.right)) {
44334447
g_currentCursor = ::LoadCursor(NULL, IDC_HAND);
44344448
m_pHoverItem = &item;
44354449
}

‎QuickView/SettingsSliderMath.h‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ struct SliderGeom {
1515
float TrackWidth() const { return trackRight - trackLeft; }
1616
};
1717

18+
// Draw uses an 8px hovered knob. Hit tests must include the full disc,
19+
// including the half that sits left of trackLeft when value == min.
20+
inline float SliderKnobHitRadius() { return 8.f; }
21+
22+
inline bool HitTestSlider(const SliderGeom& g, float x, float y,
23+
float itemTop, float itemBottom, float itemRight) {
24+
const float r = SliderKnobHitRadius();
25+
const float left = (std::min)(g.trackLeft, g.knobX) - r;
26+
const float right = (std::max)(itemRight, g.trackRight + r);
27+
return x >= left && x <= right && y >= itemTop && y <= itemBottom;
28+
}
29+
1830
inline float QuantizeSliderValue(float v, float minV, float maxV, float step) {
1931
if (maxV < minV) std::swap(minV, maxV);
2032
v = std::clamp(v, minV, maxV);

‎tests/SettingsSliderMathTests.cpp‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ TEST(SettingsSliderMath, IntegerStepHits375) {
2626
EXPECT_FLOAT_EQ(QuantizeSliderValue(640.f, 190.f, 800.f, 1.f), 640.f);
2727
}
2828

29+
TEST(SettingsSliderMath, MinKnobIsHittableLeftOfTrack) {
30+
const auto g = ComputeSliderGeom(400.f, 10.f, 40.f, 1.f, 190.f, 190.f, 800.f);
31+
EXPECT_FLOAT_EQ(g.knobX, g.trackLeft);
32+
EXPECT_TRUE(QuickView::HitTestSlider(g, g.knobX - 6.f, 25.f, 10.f, 40.f, 400.f));
33+
EXPECT_FALSE(QuickView::HitTestSlider(g, g.knobX - 20.f, 25.f, 10.f, 40.f, 400.f));
34+
}
35+
2936
TEST(SettingsSliderMath, EffectiveStepMatchesWheelHeuristic) {
3037
EXPECT_FLOAT_EQ(EffectiveStep(0.f, 10.f, 100.f, L"%.0f %%"), 1.f);
3138
EXPECT_FLOAT_EQ(EffectiveStep(0.f, 0.f, 1.f, L"%.0f %%"), 0.01f);

0 commit comments

Comments
 (0)