Clear stale hover on capture-loss and Alt-delete; derive gutter test via chromeRects

This commit is contained in:
2026-07-30 10:39:27 -04:00
parent 9812690b96
commit e89568c1a8
5 changed files with 26 additions and 7 deletions
+1 -1
View File
@@ -1034,7 +1034,7 @@ target_link_libraries(capture_browser_tests PRIVATE capture_browser)
add_test(NAME capture_browser_tests COMMAND capture_browser_tests)
add_executable(keyboard_strip_tests tests/test_keyboard_strip.cpp)
target_link_libraries(keyboard_strip_tests PRIVATE keyboard_strip sample_bands)
target_link_libraries(keyboard_strip_tests PRIVATE keyboard_strip sample_bands sample_chrome)
add_test(NAME keyboard_strip_tests COMMAND keyboard_strip_tests)
# waveform_view (S11): the pure marker geometry + zero-crossing snap. Links waveform_view (+
+4 -1
View File
@@ -82,7 +82,10 @@ void ReaSamplerEditor::onMouseUp(int x, int y) {
// hover_ is deliberately not re-resolved during a drag (see resolveHover's caller), so it
// still names wherever the drag started. Re-resolve now against the release position, for
// every drag kind — otherwise the next paint latches a stale hover (wrong note name/tooltip,
// wrong control outline) until the next WM_MOUSEMOVE.
// wrong control outline) until the next WM_MOUSEMOVE. This resolve runs before the release
// branches below can delete or relocate the hovered element; a future branch that does so
// must clear hover_ itself afterwards (as the curve drag-off delete does below) rather than
// rely on this resolve, which reflects pre-mutation state.
resolveHover(x, y);
// A scrollbar drag is transient UI (no parameter change), and the processor-side knobs
// (the preview-velocity -2 sentinel, voice count, master gain) are per-instance settings
+4 -1
View File
@@ -47,7 +47,10 @@ void ReaSamplerEditor::handleCurveMouseDown(const Rect& r, int x, int y) {
// Modifier-click (Alt) deletes an interior node — a discrete, final edit committed at once
// (deletePoint refuses the two endpoints, so an Alt-click on them is a safe no-op).
if (idx >= 0 && (GetKeyState(VK_MENU) & 0x8000) != 0) {
if (params_.velocityCurve.deletePoint(static_cast<std::size_t>(idx))) commitAndReload();
if (params_.velocityCurve.deletePoint(static_cast<std::size_t>(idx))) {
hover_ = HoverTarget{}; // a stale kCurveNode index would light a shifted node
commitAndReload();
}
return;
}
+4
View File
@@ -231,6 +231,10 @@ LRESULT CALLBACK ReaSamplerEditor::wndProc(HWND hwnd, UINT msg, WPARAM wParam,
self->drag_ = DragKind::kNone;
self->dragParamId_ = -1;
self->curvePointIndex_ = -1; // curve-node drag state (peer reset)
// No cursor position is available here to re-resolve hover (unlike
// onMouseUp's release coordinates), so clear rather than leave it naming
// wherever the drag started.
self->hover_ = HoverTarget{};
self->invalidate();
}
}
+13 -4
View File
@@ -16,9 +16,11 @@
// unverified by this suite (see src/core/instrument/CLAUDE.md).
#include "../src/core/instrument/ui/keyboard_strip.h"
#include "../src/core/instrument/ui/sample_bands.h" // kPad, to derive the strip width the
// shipped default window (840x620,
#include "../src/core/instrument/ui/sample_bands.h" // computeSampleBands, to derive the CHROME
// band the shipped default window (840x620,
// editor_session.cpp) hands the strip
#include "../src/core/instrument/ui/sample_chrome.h" // chromeRects, to derive rootStrip's width
// the same way the shell does
#include <cstdio>
#include <string>
@@ -288,8 +290,15 @@ static void testNoteNamesFollowTheC4Convention() {
// one pixel of resize (840->841) collapses both gutters to zero and grows every white key
// from 10px to 11px.
static void testGutterAtTheShippedDefaultWindowSize() {
constexpr int kShippedDefaultWindowW = 840; // editor_session.cpp's ViewRect default
const int stripW = kShippedDefaultWindowW - 2 * kPad; // chromeRects insets rootStrip by kPad
constexpr int kShippedDefaultWindowW = 840; // editor_session.cpp's ViewRect default
constexpr int kShippedDefaultWindowH = 620; // editor_session.cpp's ViewRect default
// Derive rootStrip's width the same way the shell does, through the real allocator +
// chrome layout, rather than re-deriving the inset formula — so a change to either one
// fails this test instead of silently moving the shipped gutter out from under it.
const SampleBands bands =
computeSampleBands(kShippedDefaultWindowW, kShippedDefaultWindowH, 0);
const ChromeRects chrome = chromeRects(bands.chrome, /*knobSize=*/24);
const int stripW = chrome.rootStrip.width;
CHECK(stripW == 824);
const StripLayout L = layoutStrip(stripW, 30);