diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7def86aaeb..9ce8d79e4d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -505,10 +505,12 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) io.MouseDown[0] = evt.LeftIsDown(); io.MouseDown[1] = evt.RightIsDown(); io.MouseDown[2] = evt.MiddleIsDown(); - float wheel_delta = static_cast(evt.GetWheelDelta()); - if (wheel_delta != 0.0f) { + // Only a vertical wheel event with a direction is a wheel step. A macOS trackpad reports sideways motion + // as separate horizontal events, and its wheel events often carry a rotation of 0: wx truncates the + // sub-pixel deltas to whole ones and passes on events with no delta at all. Read as vertical steps, + // either kind scrolls against the gesture, and scrolling up bounces back and forth. + if (evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL && evt.GetWheelRotation() != 0) io.MouseWheel = evt.GetWheelRotation() > 0 ? 1.f : -1.f; - } unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0); m_mouse_buttons = buttons; diff --git a/tests/slic3rutils/CMakeLists.txt b/tests/slic3rutils/CMakeLists.txt index ee556f8d86..e740ce95f0 100644 --- a/tests/slic3rutils/CMakeLists.txt +++ b/tests/slic3rutils/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(${_TEST_NAME}_tests test_texture_color_palette.cpp test_texture_island_view.cpp test_file_url.cpp + test_imgui_wrapper.cpp test_user_manager.cpp ../fff_print/test_helpers.cpp ) diff --git a/tests/slic3rutils/test_imgui_wrapper.cpp b/tests/slic3rutils/test_imgui_wrapper.cpp new file mode 100644 index 0000000000..f24e371cd2 --- /dev/null +++ b/tests/slic3rutils/test_imgui_wrapper.cpp @@ -0,0 +1,61 @@ +// How ImGuiWrapper turns wx mouse events into ImGui input. ImGuiWrapper lives in libslic3r_gui; this is +// the suite that links it. Same Windows include prologue as test_texture_color_palette.cpp (wx pulls in +// ; keep WIN32_LEAN_AND_MEAN / NOMINMAX ahead of it). +#ifdef WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include +#endif + +#include +#include +#include +#include + +#include +#include + +#include "slic3r/GUI/ImGuiWrapper.hpp" + +using Catch::Matchers::WithinAbs; +using Slic3r::GUI::ImGuiWrapper; + +namespace { + +// ImGui's wheel after one wheel event, as wx on macOS builds it: a delta of 10 and the rotation in whole +// points of trackpad travel. +float wheel_after(int rotation, wxMouseWheelAxis axis) +{ + ImGuiWrapper imgui; + imgui.set_display_size(800.f, 600.f); // update_mouse_data() ignores input until the display has a size + + wxMouseEvent evt(wxEVT_MOUSEWHEEL); + evt.m_wheelRotation = rotation; + evt.m_wheelDelta = 10; + evt.m_wheelAxis = axis; + imgui.update_mouse_data(evt); + return ImGui::GetIO().MouseWheel; +} + +} // namespace + +TEST_CASE("a vertical wheel event steps ImGui's wheel in its own direction", "[ImGuiWrapper]") +{ + const int rotation = GENERATE(1, 7, 120, -1, -7, -120); + CHECK_THAT(wheel_after(rotation, wxMOUSE_WHEEL_VERTICAL), WithinAbs(rotation > 0 ? 1.0 : -1.0, 0.0)); +} + +// Both kinds arrive from a macOS trackpad while scrolling; see ImGuiWrapper::update_mouse_data(). +TEST_CASE("a wheel event with no rotation or on the horizontal axis does not step ImGui's wheel", "[ImGuiWrapper][Regression]") +{ + const auto [rotation, axis] = GENERATE(table({ + { 0, wxMOUSE_WHEEL_VERTICAL }, + { 5, wxMOUSE_WHEEL_HORIZONTAL }, + { -5, wxMOUSE_WHEEL_HORIZONTAL }, + })); + CHECK_THAT(wheel_after(rotation, axis), WithinAbs(0.0, 0.0)); +}