From 2939abf5f876a7454e0afbc06b32c888989b6546 Mon Sep 17 00:00:00 2001 From: Koen Schmeets Date: Mon, 23 Nov 2020 22:11:14 +0100 Subject: [PATCH] Improve zooming with trackpad and scrollwheel On macOS actual scroll delta is used for the zoom amount. On Windows WHEEL_DELTA is used to allow smooth scrolling if supported. Shift+Scroll is added for 10x finer zooming. --- CHANGELOG.md | 1 + src/mouse.cpp | 22 ++++++++++++++-------- src/platform/guigtk.cpp | 4 ++-- src/platform/guimac.mm | 7 +++---- src/platform/guiwin.cpp | 2 +- src/textwin.cpp | 15 ++++++++++++--- src/ui.h | 2 +- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b302f403..8447d0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ Other new features: that are shortcuts to the respective configuration screens. * New cmake build options using -DENABLE_OPENMP=yes and -DENABLE_LTO=yes to enable support for multi-threading and link-time optimization. + * "Shift+Scroll" for ten times finer zoom. Bugs fixed: * Fixed broken --view options for command line thumbnail image creation. diff --git a/src/mouse.cpp b/src/mouse.cpp index 6c22a0cb..729c8ee2 100644 --- a/src/mouse.cpp +++ b/src/mouse.cpp @@ -877,7 +877,6 @@ bool GraphicsWindow::ConstrainPointByHovered(hEntity pt, const Point2d *projecte bool GraphicsWindow::MouseEvent(Platform::MouseEvent event) { using Platform::MouseEvent; - double width, height; window->GetContentSize(&width, &height); @@ -918,7 +917,7 @@ bool GraphicsWindow::MouseEvent(Platform::MouseEvent event) { break; case MouseEvent::Type::SCROLL_VERT: - this->MouseScroll(event.x, event.y, (int)event.scrollDelta); + this->MouseScroll(event.x, event.y, event.shiftDown ? event.scrollDelta / 10 : event.scrollDelta); break; case MouseEvent::Type::LEAVE: @@ -1472,18 +1471,25 @@ void GraphicsWindow::EditControlDone(const std::string &s) { } } -void GraphicsWindow::MouseScroll(double x, double y, int delta) { +void GraphicsWindow::MouseScroll(double x, double y, double delta) { double offsetRight = offset.Dot(projRight); double offsetUp = offset.Dot(projUp); double righti = x/scale - offsetRight; double upi = y/scale - offsetUp; - if(delta > 0) { - scale *= 1.2; - } else if(delta < 0) { - scale /= 1.2; - } else return; + // The default zoom factor is 1.2x for one scroll wheel click (delta==1). + // To support smooth scrolling where scroll wheel events come in increments + // smaller (or larger) than 1 we do: + // scale *= exp(ln(1.2) * delta); + // to ensure that the same total scroll delta always results in the same + // total zoom irrespective of in how many increments the zoom was applied. + // For example if we scroll a total delta of a+b in two events vs. one then + // scale * e^a * e^b == scale * e^(a+b) + // while + // scale * a * b != scale * (a+b) + // So this constant is ln(1.2) = 0.1823216 to make the default zoom 1.2x + scale *= exp(0.1823216 * delta); double rightf = x/scale - offsetRight; double upf = y/scale - offsetUp; diff --git a/src/platform/guigtk.cpp b/src/platform/guigtk.cpp index 287515eb..e8113ed2 100644 --- a/src/platform/guigtk.cpp +++ b/src/platform/guigtk.cpp @@ -472,7 +472,7 @@ protected: } bool process_pointer_event(MouseEvent::Type type, double x, double y, - guint state, guint button = 0, int scroll_delta = 0) { + guint state, guint button = 0, double scroll_delta = 0) { MouseEvent event = {}; event.type = type; event.x = x; @@ -536,7 +536,7 @@ protected: } bool on_scroll_event(GdkEventScroll *gdk_event) override { - int delta; + double delta; if(gdk_event->delta_y < 0 || gdk_event->direction == GDK_SCROLL_UP) { delta = 1; } else if(gdk_event->delta_y > 0 || gdk_event->direction == GDK_SCROLL_DOWN) { diff --git a/src/platform/guimac.mm b/src/platform/guimac.mm index 97897364..b2b07a8b 100644 --- a/src/platform/guimac.mm +++ b/src/platform/guimac.mm @@ -554,7 +554,9 @@ MenuBarRef GetOrCreateMainMenu(bool *unique) { MouseEvent event = [self convertMouseEvent:nsEvent]; event.type = MouseEvent::Type::SCROLL_VERT; - event.scrollDelta = [nsEvent deltaY]; + + bool isPrecise = [nsEvent hasPreciseScrollingDeltas]; + event.scrollDelta = [nsEvent scrollingDeltaY] / (isPrecise ? 50 : 5); if(receiver->onMouseEvent) { receiver->onMouseEvent(event); @@ -975,9 +977,6 @@ public: if(GetScrollbarPosition() == pos) return; [nsScroller setDoubleValue:(pos / (ssView.scrollerMax - ssView.scrollerMin))]; - if(onScrollbarAdjusted) { - onScrollbarAdjusted(pos); - } } void Invalidate() override { diff --git a/src/platform/guiwin.cpp b/src/platform/guiwin.cpp index cc930775..ebd2667b 100644 --- a/src/platform/guiwin.cpp +++ b/src/platform/guiwin.cpp @@ -925,7 +925,7 @@ public: event.y = pt.y / pixelRatio; event.type = MouseEvent::Type::SCROLL_VERT; - event.scrollDelta = GET_WHEEL_DELTA_WPARAM(wParam) > 0 ? 1 : -1; + event.scrollDelta = GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA; break; case WM_MOUSELEAVE: diff --git a/src/textwin.cpp b/src/textwin.cpp index e243825c..3e339226 100644 --- a/src/textwin.cpp +++ b/src/textwin.cpp @@ -252,8 +252,18 @@ void TextWindow::Init() { MouseLeave(); return true; } else if(event.type == MouseEvent::Type::SCROLL_VERT) { - ScrollbarEvent(window->GetScrollbarPosition() - - LINE_HEIGHT / 2 * event.scrollDelta); + if (event.scrollDelta == 0) { + return true; + } + if (abs(event.scrollDelta) < 0.2) { + if (event.scrollDelta > 0) { + event.scrollDelta = 0.2; + } else { + event.scrollDelta = -0.2; + } + } + double offset = LINE_HEIGHT / 2 * event.scrollDelta; + ScrollbarEvent(window->GetScrollbarPosition() - offset); } return false; }; @@ -1148,7 +1158,6 @@ void TextWindow::ScrollbarEvent(double newPos) { int bottom = top[rows-1] + 2; newPos = min((int)newPos, bottom - halfRows); newPos = max((int)newPos, 0); - if(newPos != scrollPos) { scrollPos = (int)newPos; window->SetScrollbarPosition(scrollPos); diff --git a/src/ui.h b/src/ui.h index 8bdd701c..6563a144 100644 --- a/src/ui.h +++ b/src/ui.h @@ -825,7 +825,7 @@ public: void MouseLeftDoubleClick(double x, double y); void MouseMiddleOrRightDown(double x, double y); void MouseRightUp(double x, double y); - void MouseScroll(double x, double y, int delta); + void MouseScroll(double x, double y, double delta); void MouseLeave(); bool KeyboardEvent(Platform::KeyboardEvent event); void EditControlDone(const std::string &s);