Add ability to constrain crop tool to current image ratio
ClosedPublic

Authored by huoni on Mar 10 2018, 5:44 AM.

Details

Summary

The crop tool was lacking the ability to choose the ratio of the current image.
This patch adds:

  • Checkbox in Normal to toggle between no restriction (default), and restrict to the current image ratio
  • "Current Image" item to the Ratio combobox in Advanced
  • "Unrestricted" item to the Ratio combobox in Advanced (default, functionally no change from before)
  • Clear button to the Ratio combobox in Advanced, to help make it clear the user can enter a custom ratio

BUG: 236970

Test Plan
  1. Image view > enable crop tool (e.g. by using +c
  2. Ensure ratio restrictions work in both Normal and Advanced

The ratio settings in Normal and Advanced are independant of
one another, in prep for future patch which will have these remember the
chosen setting.

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.
huoni requested review of this revision.Mar 10 2018, 5:44 AM
huoni created this revision.
ngraham requested changes to this revision.Mar 12 2018, 3:23 AM

So my preference here would not be to make it the default setting in basic mode. The reason being that people not using Advanced mode might not want to constrain the proportions to those of the current image, and shouldn't have to figure out that they need to open the Advanced settings to turn that off.

Since this is bound to be a popular and frequently used--and toggled--feature, perhaps we could put a checkbox to control it in the Basic view, and then put it in the Ratio menu in the Advanced view, the way you currently have it, and then have the Advanced view remember the last-used setting.

This revision now requires changes to proceed.Mar 12 2018, 3:23 AM
huoni added a comment.EditedMar 12 2018, 4:21 AM

So my preference here would not be to make it the default setting in basic mode. The reason being that people not using Advanced mode might not want to constrain the proportions to those of the current image, and shouldn't have to figure out that they need to open the Advanced settings to turn that off.

Since this is bound to be a popular and frequently used--and toggled--feature, perhaps we could put a checkbox to control it in the Basic view, and then put it in the Ratio menu in the Advanced view, the way you currently have it, and then have the Advanced view remember the last-used setting.

Yeah thought as much regarding default. Good idea to have a toggle in basic view. Should we also remember the last used basic view setting? That would satisfy the users that want Current Image as default.

What do you think of this mockup? Opinion on the wording?

Advanced view remember the last-used setting

Do you mean store this in the config, or just remember between toggling Advanced on and off?
Is it a good idea to have the ratio potentially change when enabling Advanced? What if the user draws a Freeform crop, then wants to tweak the position/size using the Advanced spin boxes. They would have to re-do it if it automatically loads the last-used ratio.

Yes, I think we should definitely remember the setting's status in Basic view. I think the phrase "Constrain proportions to current image" would be more natural.

I believe whether Advanced is active is already remembered, so I was referring to the last-used entry in the drop-down menu.

I'll have to think about your last comment.

Just an idea and probably out of focus here: Should we add some kind of shortcut to e.g. square and/or image dimension selection with Ctrl/ + mouse drag like some image manipulation programs do?

An interesting topic and not straightforward, but here is how I imagine it should work:

  • Normal mode
    • No aspect ratio lock by default.
    • Restrict/Constrain to image ratio checkbox shown, remember state for next invocation.
    • Hold modifier to lock current aspect ratio temporarily. (Gimp and LibreOffice use , Inkscape uses Ctrl – we could simply use both for now…)
  • Advanced mode
    • Restrict/Constrain to image ratio checkbox hidden.
    • For "Unrestricted" (perhaps this is a better wording that "Freeform"?), provide modifier key as above.
    • Remember last used mode for next invocation of Crop, save in config file. Default to "Unrestricted" if never used before.
    • When the next image switched its dimensions from portrait to landscape and vice versa, the last selected ratio should switch automatically too.
    • Options in addition to current ones: "Unrestricted", "Current Image"
    • Ensure that users can provide custom ratios (e.g. 3:7)and those are remembered properly as long as the ratio is not changed to something else. This still works as before, but is not really discoverable. Let's add an inline Clear button to the editable combobox.
  • Note the absence of any new option in Gwenview's settings dialog.

This should resolve most of your concerns, and it would then also solve BUG: 391757 which was reported just today.

In case these suggestions are agreed upon, I'd suggest to split the implementation into multiple patches.

Is it a good idea to have the ratio potentially change when enabling Advanced? What if the user draws a Freeform crop, then wants to tweak the position/size using the Advanced spin boxes. They would have to re-do it if it automatically loads the last-used ratio.

I would not worry about that, because we already change around the crop box quite a bit when changing ratios. There is no good solution to this.

I think the phrase "Constrain proportions to current image" would be more natural.

Sadly that's much too long. Adding the checkbox will already increase the horizontal window size in normal mode.

huoni added a comment.EditedMar 13 2018, 3:03 AM

In case these suggestions are agreed upon, I'd suggest to split the implementation into multiple patches.

Sounds like a good solution to me. Regarding splitting this up, does the following separation make sense?

  1. Add the checkbox to Normal, and add "Unrestricted" and "Current Image" to Advanced. Set "Unrestricted" (or unchecked) as default for both.
  2. Save chosen settings in config, i.e. remember last used settings
  3. Add / ctrl modifiers

+1 for Henrik's approach. That makes sense to me.

rkflx added a comment.Mar 13 2018, 8:32 PM

Sounds like a good solution to me. Regarding splitting this up, does the following separation make sense?

  1. Add the checkbox to Normal, and add "Unrestricted" and "Current Image" to Advanced. Set "Unrestricted" (or unchecked) as default for both.
  2. Save chosen settings in config, i.e. remember last used settings
  3. Add / ctrl modifiers

Sounds good from a high level, adapt as needs evolve. Perhaps you'll find dependent revisions useful (you can just use arc feature instead of the equivalent but wordy git ... --track ...).

BTW, I didn't mean you have to implement this all by yourself (unless you want to, of course). You could also try to get others to help you.

huoni added a comment.EditedMar 15 2018, 5:36 AM

When the next image switched its dimensions from portrait to landscape and vice versa, the last selected ratio should switch automatically too

This is going to require some re-working of how we store the different ratios I think.

When loading the config we can find the corresponding ratio from the combobox, check whether the ratio is portrait or lanscape and switch accordingly. But this would also switch the dimensions of Current Image, This Screen, etc. In other words, there's no way to determine whether a ratio is under the Portrait or Landscape heading in the combobox without a re-work of how we manage the ratios.

I guess my question is, is it worth re-working how we store the ratios in order to accommodate this? I'm thinking something like a list of custom objects that contain some metadata in addition to a ratio would be necessary.
Any other ideas?

I guess my question is, is it worth re-working how we store the ratios in order to accommodate this? I'm thinking something like a list of custom objects that contain some metadata in addition to a ratio would be necessary.
Any other ideas?

The QComboBox stores QSizeF values as user data. Maybe it's possible to search for QSizeF::transposed() on a portrait/landscape change?
IMHO a basic change/rewrite of storing and handling the combo box entries stands in no relation to the benefits.

I guess my question is, is it worth re-working how we store the ratios in order to accommodate this? I'm thinking something like a list of custom objects that contain some metadata in addition to a ratio would be necessary.
Any other ideas?

The QComboBox stores QSizeF values as user data. Maybe it's possible to search for QSizeF::transposed() on a portrait/landscape change?

QSizeF::transposed() simply returns a new object with width/height swapped. I don't see how that would help determine which combobox items were under which Landscape/Portrait heading.

IMHO a basic change/rewrite of storing and handling the combo box entries stands in no relation to the benefits.

Sorry not sure what you mean. Are you saying that a change/rewrite is fine to go ahead with regardless?

When the next image switched its dimensions from portrait to landscape and vice versa, the last selected ratio should switch automatically too

This is going to require some re-working of how we store the different ratios I think.

I figured it would be a long shot ;) I'd say focus on the rest first, this was probably more of an optional idea…

Essentially this would need storing whether a given custom or predefined ratio was meant for a landscape or a portrait image (or rather normalize it to one variant), and inverting the ratio once an image has a different orientation.

The underlying question is what users will expect: I'd imagine for cropping to ISO/A4/… automatically changing the ratio would be handy. On the other hand, in other cases this might be annoying.

Anyway, I split this off into a bug for now, so we won't get too sidetracked here:
Bug 391884 – Investigate whether crop ratios should be inverted when image orientation changes

The underlying question is what users will expect: I'd imagine for cropping to ISO/A4/… automatically changing the ratio would be handy. On the other hand, in other cases this might be annoying.

I considered the same thing. As stated in the bug, I think we should leave this out for now, and re-visit once all other changes are implemented.

huoni updated this revision to Diff 29654.Mar 16 2018, 5:45 AM
huoni retitled this revision from Add Freeform and Current Image ratio options to crop tool and make Current Image default to Add ability to constrain crop tool to current image ratio.
huoni edited the summary of this revision. (Show Details)
huoni edited the test plan for this revision. (Show Details)
  • Set Unrestricted as default
  • Add checkbox to Basic view to toggle between Unrestricted and Current Image
huoni added a comment.EditedMar 16 2018, 6:01 AM

> BTW, I didn't mean you have to implement this all by yourself (unless you want to, of course). You could also try to get others to help you.

No need :)

I've submitted all changes (plus a small bug fix I noticed) in a dependency chain:
D11202: Add ability to constrain crop tool to current image ratio
D11378: Save last used crop ratio settings
D11379: Fix crop ratio combobox not updating crop when text didn't change
D11380: Lock crop ratio to current rect when holding Ctrl/Shift

Sorry for giving you more work @rkflx ! No rush :)

rkflx requested changes to this revision.Mar 19 2018, 12:07 AM

Thanks for splitting up your patches, this makes them so much easier to review ;) Let's see if we can make the 18.04 Beta on Thursday (only bugfixes allowed after that), but I also don't want to rush this so it might only end up in 18.08…

Sorry for giving you more work

Sorry for setting the red light on all four patches :D (It's not that bad, though…)


Most things worked really great when testing, here are some niggles to make the feature perfect (apologies if an issue belongs to a different Diff, perhaps some points can even be resolved later):

  • For Current Image, the text in the combobox is cut off. Maybe the width calculation is now wrong because of the Clear button? Or can we at least cut off on the right instead of on the left?
  • When rotating while the Crop tool is active, Current Image does not adapt to the new orientation (note this is different to the navigation case from above).
  • I wonder if we could just leave out Unrestricted completely (and save/restore the "empty" state), because the Clear button seems to work really well from a UI perspective.
lib/crop/cropwidget.cpp
73–103

Could you instead refactor static QSize screenRatio() into static QSize ratio(QSize)?

133–134

Could be written as else if, but is this whole nesting really necessary? You always return, so this could be simplified:

if()
  return
if()
  return
return
134

This is the same as q->restrictToImageRatio(), isn't it?

229–230

When changing code, let's directly use the "modern" connect syntax.

234–235

Please use the "modern" connect syntax here too.

354–357

Do you really need this as a separate function? applyRatioConstraint should also be slot and thus could be connected directly.

This revision now requires changes to proceed.Mar 19 2018, 12:07 AM
huoni updated this revision to Diff 29880.Mar 19 2018, 3:56 AM
  • Fix width of combobox clipping text
  • Update crop ratios when image rect updated (e.g. when image rotated)
  • Remove "Unrestricted" in favour of using clear button
huoni updated this revision to Diff 29881.Mar 19 2018, 4:18 AM
  • Code cleanup
huoni marked 5 inline comments as done.Mar 19 2018, 4:26 AM

Sorry for setting the red light on all four patches :D (It's not that bad, though…)

That's okay, thanks for taking the time to review!

I've addressed all your issues I believe. I'm not certain about a couple of things though:

  1. The combobox width fix (see inline comment)
  2. The way I am updating the ratios when the image rect changes (when rotating)

I don't mind missing the 18.04 feature freeze, happy to take this one slow :)

lib/crop/cropwidget.cpp
134

I added that method in a different patch. I will update said patch, changing this line to use the new method.

181

This is a hack. Apparently the autosizing doesn't take into account the clear button, but hardcoding a value here is obviously not ideal.
I don't know enough about HiDPI scaling to figure out a way to base this on the DPI. Qt recommends calculating sizes from fonts but I don't know how to do that.

354–357

I was following the example of slotRatioComboBoxEditTextChanged. I have now removed both, and connected them strait to applyRatioConstraint instead.

huoni updated this revision to Diff 29882.Mar 19 2018, 4:38 AM
huoni marked an inline comment as done.
  • Remove init combobox with first item selected

The way I am updating the ratios when the image rect changes (when rotating)

Well, it breaks for custom ratios (might be related to D11378#229012). Apart from that, it does not look too wrong. Maybe you could only update the value for Current Image?

lib/crop/cropwidget.cpp
73

As this does not touch any member variables, could this be kept static like screenRatio was before?

75

return size / divisor should work too.

105

Is size() available for mDocument?

147

Is size() available for mDocument?

181

Apparently the autosizing doesn't take into account the clear button

I guess that's because of the way the combobox is turned into a lineedit.

This is a hack.

…and for "ISO" it's not even working for me (perhaps you have a different clear icon?). Ideally this should be fixed in Qt, for now let's change this to 24 and be done with it. (There might be another way involving findChild and the QLineEditIconButton I discovered in GammaRay, but the minimumSizeHint of that one was 32, so too large.) It works fine for me on HiDPI (QT_SCALE_FACTOR=1.5 gwenview), because the values you set are in "virtual" pixel, so they will be scaled up too.

calculating sizes from fonts

I'd assume this meant QFontMetrics. Anyway, the icon seems to be the problem, and not the text.

354–357

No problem, that's a good strategy in general ;) However, I feel sometimes the old code is quite verbose and too nested.

huoni updated this revision to Diff 29959.Mar 20 2018, 1:58 AM
huoni marked 5 inline comments as done.
  • Code cleanup
  • Only update "Current Image" ratio on image rect update (e.g. rotate)
huoni added a comment.EditedMar 20 2018, 1:58 AM

Thanks again, a few code tricks I missed there.

There is a small bug where if you open the crop tool in Advanced, leave the Ratio blank, and rotate an image, the Ratio changes to the "Current Image" and the crop adjust accordingly.
I figured this would be fixed in D11378 anyway, so have left it.

EDIT: latest diff actually fixes this.

lib/crop/cropwidget.cpp
181

because the values you set are in "virtual" pixel, so they will be scaled up too.

Didn't know that!

Also noticed the clear button icon itself doesn't scale anyway....

huoni updated this revision to Diff 29962.Mar 20 2018, 5:19 AM
  • Fix crop rect not readjusting after rotating in all cases
rkflx requested changes to this revision.Mar 21 2018, 9:47 PM

The patch series is really coming together nicely, only some finishing touches left.

lib/crop/cropwidget.cpp
181

Also noticed the clear button icon itself doesn't scale anyway....

Weird, for me it does scale perfectly, but I'm also only testing with the default Breeze icon set. I don't think that's something for this patch to figure out.

337–338

After adding the item for "Current image", you could count() and store the index in a constant.

It's not perfect, but better than assuming nobody will ever change things around. Grepping for a C++ construct is always easier than searching for a random comment you are not even aware of.

This revision now requires changes to proceed.Mar 21 2018, 9:47 PM
huoni updated this revision to Diff 30165.Mar 22 2018, 12:31 AM
  • Store Current Image index in member variable
  • Clarify comment wording
lib/crop/cropwidget.cpp
337–338

That does sound like a better solution. Though I don't see how we can make it a constant, given we need to declare and initialize the variable separately.

rkflx accepted this revision.Mar 22 2018, 10:50 AM

Thanks @huoni for this nice feature and following through with all the changes and separate patches… It's a nice addition which I think should ship in the Beta. There is still the RC and the final release, so enough time to fix things in case we missed a bug.

Not sure when you'll be around again, otherwise I can land it for you around 23:00 UTC.

lib/crop/cropwidget.cpp
337–338

Oops, you are right of course. There is no write-once type of variable built in, and we should also not roll our own.

rkflx removed a reviewer: ngraham.EditedMar 22 2018, 10:53 AM

Sorry Nate, I'm removing you only so that Huon can land this, because you still requested changes. Feel free to test and possibly accept again, though ;)

+1 for Henrik's approach. That makes sense to me.

Huon implemented all of that, so I assume you are okay with the patch…

This revision is now accepted and ready to land.Mar 22 2018, 10:53 AM

Thanks @huoni for this nice feature and following through with all the changes and separate patches… It's a nice addition which I think should ship in the Beta. There is still the RC and the final release, so enough time to fix things in case we missed a bug.

Not sure when you'll be around again, otherwise I can land it for you around 23:00 UTC.

Can land now. If you're still around, should I merge stable into master after each patch, or just do one merge after landing all of them?

should I merge stable into master after each patch, or just do one merge after landing all of them?

A single merge should be enough.

This revision was automatically updated to reflect the committed changes.
huoni added a comment.EditedMar 22 2018, 11:18 AM

should I merge stable into master after each patch, or just do one merge after landing all of them?

A single merge should be enough.

I seem to be having an issue rebasing the second patch. Getting a bunch of weird conflicts when I try to rebase onto origin/Applications/18.04. First CMakeLists.txt with the minor version, then continuing the rebase causes 14 more conflicts.
Would you be able to help if you're still here?
EDIT: I've merged stable into master for now so I don't leave them diverged.

I guess the problem is that you are simultaneously trying to rebase onto stable (for which the --onto switch is, i.e. to avoid the conflict with the different version strings for both branches) and at the same time you are using the instructions for landing dependent patches. Perhaps you just need this extra argument.

You can try this, otherwise I'll try to recreate the problem and figure something out.

EDIT: I've merged stable into master for now so I don't leave them diverged.

It's no problem if they are not in sync for a some hours. Eventually someone will merge them again…

Okay great. Everything looks good to me in the repo. Good job ;)

What was the final command that did the trick for you? Pondering about changes to the docs…

I guess the problem is that you are simultaneously trying to rebase onto stable (for which the --onto switch is, i.e. to avoid the conflict with the different version strings for both branches) and at the same time you are using the instructions for landing dependent patches. Perhaps you just need this extra argument.

You can try this, otherwise I'll try to recreate the problem and figure something out.

EDIT: I've merged stable into master for now so I don't leave them diverged.

It's no problem if they are not in sync for a some hours. Eventually someone will merge them again…

I got them landed properly by running git rebase --skip for all the commits that were already landed. For some reason for patch 2-4, rebasing onto stable was trying to replay the versio change commit 0001 GIT_SILENT Upgrade KDE Applications version to 18.07.70., and then all the commits from the parent patches.
I'm not understanding something here, but firstly, I thought git automatically skipped replaying commits if upstream had them. And secondly, I don't know where that first version change commit is coming from.

I tried using --onto but it didn't seem to make a difference. Perhaps I was still not doing it right.

Thanks, I'll make a note to investigate this later.

Thanks, I'll make a note to investigate this later.

If it helps, here is the shell output from rebasing the last patch.

Yep, +1, looks good to me! A very nice feature.

rkflx added a comment.Mar 22 2018, 9:31 PM

@huoni I just checked out the Photos app in Windows 10, they call this aspect ratio "Original" instead of "Current Image", which I find really great. Thoughts? Now/later/never?

@huoni I just checked out the Photos app in Windows 10, they call this aspect ratio "Original" instead of "Current Image", which I find really great. Thoughts? Now/later/never?

It's possible to make crop, then crop again. At the moment, Current Image is the ratio of the last crop. "Original" would suggest it was the original image's ratio, so we'd have to think about changing that functionality I think.

@huoni I just checked out the Photos app in Windows 10, they call this aspect ratio "Original" instead of "Current Image", which I find really great. Thoughts? Now/later/never?

It's possible to make crop, then crop again. At the moment, Current Image is the ratio of the last crop. "Original" would suggest it was the original image's ratio, so we'd have to think about changing that functionality I think.

Okay, too bad. It's an edge case, but let's keep what we have for now.