Scroll wheel and touchpad zoom in smaller increments
ClosedPublic

Authored by ngraham on Sep 24 2017, 5:09 PM.

Details

Summary

BUG: 307637

Change the zoom speed so that the scroll wheel and touchpad zoom behavior matches that of Inkscape and Krita: for more than 100%, the zoom is increased by sqrt(2); for less than 100%, the zoom is decreased by sqrt(0.5). In practice this means the following zoom levels:

35% → 50% → 71% → 100% → 141% → 200% → 283% → 400% → 566% → 800%

This feels much more comfortable and offers more control compared to the current ever-accelerating zoom speed.

Submitting this patch on behalf of Vangelis Tasoulas.

Test Plan

Tested in KDE Neon. Works as advertised.

Diff Detail

Repository
R260 Gwenview
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
There are a very large number of changes, so older changes are hidden. Show Older Changes
ngraham updated this revision to Diff 19925.Sep 26 2017, 4:37 AM

Oops, accidentally added another patch to this one. Backing that out.

rkflx added a subscriber: rkflx.Sep 26 2017, 8:30 AM

Have not tested both yet, but I was wondering whether/how this is related to D7744? The linked bugs suggest some relation, not sure in what direction. Maybe you could clarify that (also for future readers of the commit message, perhaps).

https://phabricator.kde.org/D7744 concerned scroll/zoom *sensitivity*. That is to say, how far your fingers have to move to generate a scroll/zoom "tick".

This patch concerns scroll/zoom speed: how much gets scrolled/zoomed per scroll tick.

Without either patch, touchpad zooming was unusable. With only https://phabricator.kde.org/D7744, it was much improved, but you still don't have much control at high zoom levels. With both patches, touchpad scrolling/zooming is a pleasure to use and offers high control. Scroll wheel zooming is improved, too.

Thanks for the explanation, makes sense and now I know what to look for. As I have no touchpad in reach, I cannot test this patch in combination with the other patch, but I hope you did test with a real touchpad?

I agree the current behaviour is not ideal, but I fear the patch makes zooming too slow for high zoom levels (only tested with a scroll wheel). Here is how others are doing it:

  • Gimp: (33% )→ 50% → (66%) → 100% → (150%) → 200% → (300%) → 400% → (550%) → 800% → …
  • Inkscape: (35%) → 50% → (71%) → 100% → (141%) → 200% → (283%) → 400% → (566%) → 800% → …
  • Digikam: (35%) → (42%) → 50% → (59%) → (71%) → (85%) → 100% → (120%) → (144%) → (173%) → 207% → (249%) → …
  • Krita: (33%) → 50% → (67%) → 100% → (141%) → 200% → (283%) → 400% → (566%) → 800% → …

I would suggest not to reinvent our own thing, but just to double the zoom level every two scroll ticks with another level in between like other graphics apps are doing (preferably like Inkscape or Krita). We could even look at their code (not sure if this needs adapting for Gwenview, though, considering our default zoom-to-fit).

For touchpads (and touchscreens), the ideal solution would be to zoom continuously like on multi-touch devices, not relying on the scroll wheel emulation. However, that's out of scope for this patch.

ngraham planned changes to this revision.Sep 27 2017, 1:15 PM

Ah, that makes sense. I agree that consistency with other programs is a more important concern, and that continuous zooming with the touchpad is ideal. I too have no idea how to do that though. I can put this on the back burner and take a look.

rkflx added a comment.Sep 28 2017, 5:07 PM

Just to clarify (after re-reading your comment): I'm not suggesting you have to implement continuous zooming (you can try to in addition to the improvements for scroll wheel users in this patch, of course). All I'm saying is this patch should change the zoom behaviour from a 100% increment per tick to doubling the zoom level every second (or third, if you like this better) tick, instead of just changing the 100% to a 25%.

vtasoulas added a comment.EditedOct 6 2017, 9:02 PM

How about the following?
Implements the zoom as Inkscape and Krita does: for more than 100% the zoom is increased by sqrt(2); for less than 100% the zoom is decreased by sqrt(0.5).

The min zoom also changes when the Gwenview window gets resized (it didn't and I can see this behavior as /buggy/). I had to override the resizeEvent for that.

diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp
index 5e131d8..3965a28 100644
--- a/lib/documentview/documentview.cpp
+++ b/lib/documentview/documentview.cpp
@@ -263,18 +263,19 @@ struct DocumentViewPrivate
         qreal min = q->minimumZoom();
 
         mZoomSnapValues.clear();
-        if (min < 1.) {
+        for (qreal zoom = sqrt(0.5); zoom >= min; zoom *= sqrt(0.5))
+            mZoomSnapValues << zoom;
+
+        if (!mZoomSnapValues.isEmpty() && mZoomSnapValues.back() > min)
             mZoomSnapValues << min;
-            for (qreal invZoom = 16.; invZoom > 1.; invZoom /= 2.) {
-                qreal zoom = 1. / invZoom;
-                if (zoom > min) {
-                    mZoomSnapValues << zoom;
-                }
-            }
-        }
-        for (qreal zoom = 1; zoom <= MAXIMUM_ZOOM_VALUE ; zoom += 1.) {
+
+        std::reverse(mZoomSnapValues.begin(), mZoomSnapValues.end());
+
+        for (qreal zoom = 1; zoom <= MAXIMUM_ZOOM_VALUE; zoom *= sqrt(2))
             mZoomSnapValues << zoom;
-        }
+
+        if (mZoomSnapValues.back() < MAXIMUM_ZOOM_VALUE)
+            mZoomSnapValues << MAXIMUM_ZOOM_VALUE;
 
         q->minimumZoomChanged(min);
     }
@@ -572,6 +573,14 @@ qreal DocumentView::zoom() const
     return d->mAdapter->zoom();
 }
 
+void DocumentView::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+    Q_UNUSED(event);
+
+    d->resizeAdapterWidget();
+    d->updateZoomSnapValues();
+}
+
 void DocumentView::wheelEvent(QGraphicsSceneWheelEvent* event)
 {
     if (d->mAdapter->canZoom() && event->modifiers() & Qt::ControlModifier) {
diff --git a/lib/documentview/documentview.h b/lib/documentview/documentview.h
index e6ecda5..c6bac4c 100644
--- a/lib/documentview/documentview.h
+++ b/lib/documentview/documentview.h
@@ -197,6 +197,7 @@ Q_SIGNALS:
 protected:
     void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0) Q_DECL_OVERRIDE;
 
+    void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE;
     void wheelEvent(QGraphicsSceneWheelEvent* event) Q_DECL_OVERRIDE;
     void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) Q_DECL_OVERRIDE;
     bool sceneEventFilter(QGraphicsItem*, QEvent*) Q_DECL_OVERRIDE;

That looks great, @vtasoulas. With that diff, this is basically your patch now. Wanna submit it for review?

Is there any way to update the current diff?

I tried but I was only getting the option to create a new revision.

In any case, feel free to update the current revision since this patch is related to this thread :)

ngraham updated this revision to Diff 20408.Oct 6 2017, 9:13 PM

Updated to @vtasoulas's diff

ngraham edited the summary of this revision. (Show Details)Oct 6 2017, 9:23 PM
gateau requested changes to this revision.Oct 7 2017, 8:33 AM
gateau added inline comments.
lib/documentview/documentview.cpp
267

style: Missing curly braces around block

269

style: Missing curly braces around block

274

style: Missing curly braces around block

274

Equal comparisons on qreal are not reliable. I would suggest using < here instead of <= and always append MAXIMUM_ZOOM_VALUE to the list (that is: remove the if (mZoomSnapValues.back() < MAXIMUM_ZOOM_VALUE). Same thing for the first loop.

277

style: Missing curly braces around block

This revision now requires changes to proceed.Oct 7 2017, 8:33 AM
ngraham updated this revision to Diff 20456.Oct 7 2017, 10:43 PM

Updating diff according to @gateau's suggestions

vtasoulas requested changes to this revision.Oct 7 2017, 11:31 PM
vtasoulas added inline comments.
lib/documentview/documentview.cpp
270–271

Do not check for equality here as well. Convert the >= comparison to >: for (qreal zoom = sqrt(0.5); zoom > min; zoom *= sqrt(0.5)) {

274

Once the comparison in line 266 has been changed, then we must append the min value to the mZoomSnapValues anyhow. So this if statement (and the curly braces) can be removed.

This revision now requires changes to proceed.Oct 7 2017, 11:31 PM
ngraham updated this revision to Diff 20457.Oct 7 2017, 11:35 PM
ngraham marked 5 inline comments as done.

Updating diff according to @vtasoulas's suggestions

vtasoulas accepted this revision.Oct 7 2017, 11:40 PM
ngraham marked 2 inline comments as done.Oct 7 2017, 11:42 PM

@vtasoulas, since this is basically your patch, I'd like to mark you as the author. Is cyberang3l@gmail.com still a good email address for you?

Yes, this is a valid email address that you can use. Thank you :)

I see elsewhere you're using vangelis@tasoulas.net. Would you prefer that?

ngraham retitled this revision from Scroll wheel and touchpad zoom in increments of 25% to Scroll wheel and touchpad zoom in smaller increments.Oct 8 2017, 4:45 AM

I have one more suggest, sqrt is a function which will be called in every iteration so just precalculate steps

const auto minStep = sqrt(0.5);
const auto maxStep = sqrt(2.0);

@ngraham yes, vangelis@tasoulas.net is my primary e-mail, but my KDE identity still uses the cyberang3l@gmail.com e-mail (which is still valid).

So if there is any need to correlate the e-mail with the KDE identiy then use the cyberang3l email. Otherwise, I prefer the vangelis@tasoulas.net.

rkflx requested changes to this revision.Oct 8 2017, 11:09 AM

Works really well, great job everyone! Only found some minor issues with the code (see below).

I'd say this could go to Applications/17.08, if you agree.


Is there any way to update the current diff?

Phabricator allows to "commandeer" revisions in this case. However, as Vangelis has no commit access anyway, we can also just keep it as it is now.

The min zoom also changes when the Gwenview window gets resized

Good catch, this was broken before indeed :)

lib/documentview/documentview.cpp
271–272

To compile with GCC 7.1, I needed #include<cmath> for sqrt.

579

Before adding a custom resizeEvent, the implementation of the base class was called. Not sure if this is really necessary here, but in general it is good practise to pass the event up the chain when hooking into it, i.e. call QGraphicsWidget::resizeEvent(event) before returning.

583

Is there any way we could directly update the Gwenview::ZoomSlider? As far as I can see the issue is not really about resizing, but about telling the ZoomSlider (which includes the QSlider and two QToolButtons) to adapt to a change in the list of possible values.

This revision now requires changes to proceed.Oct 8 2017, 11:09 AM

The referenced bug is more a wishlist, so I suspect that this is more for master than the stable branch.

rkflx added a comment.Oct 8 2017, 12:13 PM

The referenced bug is more a wishlist

Well, the minimum value not updating is more of a bug 😛
Anyway, probably it's better not to change the zoom behaviour too much for a bugfix release. @ngraham should decide whether it works good enough for touchpad users with only D7744, or if this patch is also required immediately.

lib/documentview/documentview.cpp
583

After testing some more, I guess all you wanted to do here was to pass along the event? If so, resizeAdapterWidget could be removed completely in favour of QGraphicsWidget::resizeEvent.

What I stumbled upon was the Toolbutton not setting its state to disabled for the max and min values. But that's a different bug altogether, as this also happens without your patch.

rkflx added a comment.Oct 8 2017, 3:08 PM

What I stumbled upon was the Toolbutton not setting its state to disabled for the max and min values.

Interim summary: There are two issues:

  • Moving the slider with the mouse disables the buttons only when changing sliding direction again. – I have a patch eliminating this lag, will post it later.
  • Triggering the "zoom out" action does not actually reach the minimum value (you need the mouse or the cursor keys for that). This needs more investigation and a separate patch, but I suspect this should also fix the window resizing case.

IOW, let's not care about the buttons not disabling in this patch and just remove resizeAdapterWidget for now (it would fix the issue, but only for resizing and not the general case, i.e. that would be a hack and not a fix).

ngraham updated this revision to Diff 20482.Oct 8 2017, 4:18 PM

Don't repeatedly recalculate sqrt values

ngraham updated this revision to Diff 20483.Oct 8 2017, 4:23 PM

Include cmath library to make gcc 7.1 happy

ngraham updated this revision to Diff 20484.Oct 8 2017, 4:29 PM
ngraham marked an inline comment as done.

Remove unnecessary DocumentView::resizeEvent() function

ngraham marked 2 inline comments as done.Oct 8 2017, 4:31 PM

Let's keep this patch focused on changing the zoom increments, and make another separate patch to address the zoom buttons not disabling properly when dragging with slider.

Once reviews are finished, if nobody objects, I'll land this on the 17.08 branch and merge to master.

lib/documentview/documentview.cpp
579

I don't think it is necessary. When I remove it, everything works just fine, including the zoom slider buttons getting enabled and disabled appropriately.

rkflx added a comment.Oct 8 2017, 4:44 PM

Remove unnecessary DocumentView::resizeEvent() function

I'm afraid you removed too much (resizeEvent instead of only resizeAdapterWidget), so now updateZoomSnapValues is missing. In "Fit" mode after increasing window width, this would result in the slider showing you can zoom out further when in reality you cannot. Here is what I imagined:

void DocumentView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    d->updateZoomSnapValues();
    QGraphicsWidget::resizeEvent(event);
}

The toolbuttons not disabling are a different issue, of course.

ngraham updated this revision to Diff 20487.Oct 8 2017, 4:57 PM

Restoring DocumentView::resizeEvent(), which we do need

Whoops, my bad. I didn't test the fit-then-resize case. Restored.

ngraham marked 2 inline comments as done.Oct 8 2017, 4:58 PM
rkflx accepted this revision.EditedOct 8 2017, 5:06 PM

Thanks!

(I guess @gateau also needs to accept, so Phab does not block arc land because of missing "Accepted" state.)

@ngraham Apparently you can choose to "Continue anyway" when arc land complains. Given all changes Aurélien requested were implemented, I'd say this can be landed. Maybe wait until Sunday, though (to allow for a week of review time).

Sounds good.

gateau requested changes to this revision.Oct 15 2017, 11:18 AM

Some minor requests, but we are getting there!

lib/documentview/documentview.cpp
78

Since those are consts, they should be using ALL_CAPS.

579

I agree with @rkflx : it might be working just fine now, but break in the future if the implementation of QGraphicsWidget::resizeEvent() changes.

This revision now requires changes to proceed.Oct 15 2017, 11:18 AM
ngraham updated this revision to Diff 20815.Oct 15 2017, 7:19 PM

Put constants in ALL CAPS

ngraham marked an inline comment as done.Oct 15 2017, 7:20 PM
ngraham added inline comments.
lib/documentview/documentview.cpp
579

This comment chain is getting pretty dense, so I'm not totally sure what you're referring to; updating Gwenview::ZoomSLider directly? If so, I'm afraid I'm not sure how to do that.

rkflx added inline comments.Oct 15 2017, 7:23 PM
lib/documentview/documentview.cpp
579

This was about QGraphicsWidget::resizeEvent(event), which you already added. All is fine, nothing more to be done :)

ngraham marked 3 inline comments as done.Oct 15 2017, 7:33 PM
ngraham added inline comments.
lib/documentview/documentview.cpp
579

OK, great. :)

ngraham marked an inline comment as done.Oct 15 2017, 8:42 PM

@gateau, all good now?

ngraham edited the summary of this revision. (Show Details)EditedOct 19 2017, 7:09 PM

Will commit on Oct 21 if I don't hear from @gateau before then, since I've addressed his remaining comments.

This revision was automatically updated to reflect the committed changes.
rkflx added a comment.Oct 21 2017, 7:17 PM

@vtasoulas, since this is basically your patch, I'd like to mark you as the author. Is cyberang3l@gmail.com still a good email address for you?

Too bad we cannot have multiple authors, as both Vangelis and Nate contributed a lot to the successful merging of this patch. Anyway, thank you to both of you, despite what the commit says now (or will if it is changed again?).

I'll commit my depending patch tomorrow.

Oh dang it, I forgot to mark @vtasoulas as the author. :-/ Yeah, I agree that joint authorship would have been ideal here, but as a consolation prize to Vangelis, since I'm marked as the author, I own it now and when it breaks, it's my fault! ;-)

@ngraham no worries about the authorship. You did at least as much for this patch, if not more when you include all the administrative work to get this patch landed :)