Fixed bugs in bookmark search that appear after exiting from submenu
ClosedPublic

Authored by nmel on Mar 31 2018, 7:00 AM.

Details

Summary

(1)
Fixed bookmark search key swallowing after exiting from submenu

This patch fixes the following problem:

  1. Open bookmark menu.
  2. Search for a folder or navigate to it so that submenu is opened.
  3. Press Esc to close the submenu.
  4. Type a key to search - it start searching but the search is reset immediately.

Although #3 sends QCloseEvent event for the submenu, #4 also sends
QCloseEvent event for the submenu (which seems to appear
as we deactivate the folder action) + sends another QCloseEvent event
for the submenu after ~200ms through QTimer for unknown reasons.

This change allows to remember the menu we are currently searching in
and ignore spurious QCloseEvent events.

(2)
Fixed active item deselection when starting search after exiting from submenu

This patch fixes the following problem:

  1. Open bookmark menu.
  2. Search for a folder or navigate to it so that submenu is opened.
  3. Press Esc to close the submenu.
  4. Type a key to search to find a match - it start searching but in a few moments active item becomes deselected.

Although #3 sends QCloseEvent event for the submenu, #4 also sends
QCloseEvent event for the submenu + sends
another QCloseEvent event for the submenu after ~200ms through QTimer
after which the active item is reset.

This change restores the active action in this case through another QTimer.

Test Plan

Try to repro the bug from commit message - should not repro after patch.
Try various searches related to folders and pay attention to what happens after.

Diff Detail

Repository
R167 Krusader
Branch
bookmark-search-submenu-fixes_arc
Lint
No Linters Available
Unit
No Unit Test Coverage
nmel requested review of this revision.Mar 31 2018, 7:00 AM
nmel created this revision.
nmel added reviewers: martinkostolny, rade.

These patches replace a suggested workaround from D11790.

After a couple of hours of debugging, I almost understand what's happening there. Check my commit messages for explanation. Initially I thought it would be easier but Qt seems to be buggy here.

Hopefully, after this fix we can finally merge the branch...

nmel edited the summary of this revision. (Show Details)Mar 31 2018, 7:11 AM
nmel updated this revision to Diff 31012.Mar 31 2018, 7:23 AM
  • Removed excessive debug print in KrBookmarkHandler::eventFilter
rade requested changes to this revision.EditedApr 1 2018, 1:03 PM

another QCloseEvent event for the submenu after ~200ms through QTimer
after which the active item is reset.

Can't have been easy figuring out that out!
Have we been extremely unlucky running in to three different Qt5 bugs developing this feature or is Qt5 just this buggy?

I do have some serious reservations regarding this fix. Using Qtimer to restore the active action feels very much like deliberately making a race condition. But that might be due to my lack of understanding what the Qtimer event is that is triggering the removal of focus and how we know that our Qtimer event will run after it (e.g is Qtimer deterministic so if two events are scheduled for time 0, the event scheduled last will always run last or something). Could you tell me what/how the Qt bug works and why this fix fixes it without causing a race condition?

I found this code quite hard too understand. As I had quite a bit number notes for how it should be changed I decided it was easier to give you patches to be applied on top of your patch rather then inline comments with vague directions as too what I wanted changed. All the patches should be applied on top of each other fyi.

First off, I feel that the if statements should be moved inside each other too more clearly show that one or the other will run

Secondly I don't like how _quicksearchMenu get's assigned values all over the place. This makes needlessly difficult too follow the logic of the code as all the if statements depend on the value of quicksearchmenu. As an example, when I first started reading this code I expected quicksearchmenu too be null after opening and closing a submenu since we didn't receive a new Qevent::show, I didn't realise it also got assigned a value whenever we searched.

and finally I feel that there are an excessive amount of debug statements than can be removed. An argument can be made too keep them until this feature is complete and ready to be merged, but they should definitely be removed before then

I found a bug. It happens with the original patch you submitted as well as with my patch.

  1. Open menu
  2. Hover the mouse over the 'popular urls' bookmark causing it to open, do not move mouse again after this.
  3. use keyboard to close menu, e.g by pressing left arrow
  4. type a letter that will match multiple bookmarks e.g 't' that matches 'TV' and 'Telemundo'

Expected outcome: the bookmark 'Telemundo' gets highlighted
Actual outcome: 'Popular urls' gets highlighted and the submenu opens again.

krusader/BookMan/krbookmarkhandler.cpp
563

Could you explain this? How do we know that this will be scheduled to run after the event that removes focus?

This revision now requires changes to proceed.Apr 1 2018, 1:03 PM
nmel marked an inline comment as done.Apr 2 2018, 12:01 AM
In D11829#238090, @rade wrote:

another QCloseEvent event for the submenu after ~200ms through QTimer
after which the active item is reset.

Can't have been easy figuring out that out!
Have we been extremely unlucky running in to three different Qt5 bugs developing this feature or is Qt5 just this buggy?

Well, this is not a surprise to me. We are controlling menus through the code + make usual interaction. They were designed for just mouse and keyboard interaction. It will be nice if someone reports all these bugs to Qt. If you have time, please do this.

I do have some serious reservations regarding this fix. Using Qtimer to restore the active action feels very much like deliberately making a race condition. But that might be due to my lack of understanding what the Qtimer event is that is triggering the removal of focus and how we know that our Qtimer event will run after it (e.g is Qtimer deterministic so if two events are scheduled for time 0, the event scheduled last will always run last or something). Could you tell me what/how the Qt bug works and why this fix fixes it without causing a race condition?

No race condition happens. UI is running in a single thread in Qt and perceptional parallelism is governed by a so called event loop. This is a good read. QTimer::singleShot(0, this, ...) is a pretty standard pattern to add an event to spontaneous event queue which runs after posted event queue we are currently in (reference). I haven't debugged Qt to find out who's resetting the focus, but I found it's done within the posted event queue when the third Close event is received.

I found this code quite hard too understand. As I had quite a bit number notes for how it should be changed I decided it was easier to give you patches to be applied on top of your patch rather then inline comments with vague directions as too what I wanted changed. All the patches should be applied on top of each other fyi.

First off, I feel that the if statements should be moved inside each other too more clearly show that one or the other will run

I agree.

Secondly I don't like how _quicksearchMenu get's assigned values all over the place. This makes needlessly difficult too follow the logic of the code as all the if statements depend on the value of quicksearchmenu. As an example, when I first started reading this code I expected quicksearchmenu too be null after opening and closing a submenu since we didn't receive a new Qevent::show, I didn't realise it also got assigned a value whenever we searched.

{F5780757}

There is a logic here, it's not that I put it all over the place to make it just work. _quicksearchMenu is assigned in 2 cases:

  1. Menu is shown, so we about to start searching.
  2. Search is started - this is needed in case submenu is closed but Show event is not sent for parent menu as this menu is visible already.

_quicksearchMenu is reset when the tracked menu is closed. Pretty straightforward, IMO.

Your patch reduces number of lines but the logic will be lost.

and finally I feel that there are an excessive amount of debug statements than can be removed. An argument can be made too keep them until this feature is complete and ready to be merged, but they should definitely be removed before then

Debug prints are needed to see what's happening as we can't debug menus with a regular debugger in X. It may be useful for bug investigation on a user side later. I'll keep them as they support the logic I described above.

I found a bug. It happens with the original patch you submitted as well as with my patch.

  1. Open menu
  2. Hover the mouse over the 'popular urls' bookmark causing it to open, do not move mouse again after this.
  3. use keyboard to close menu, e.g by pressing left arrow
  4. type a letter that will match multiple bookmarks e.g 't' that matches 'TV' and 'Telemundo' Expected outcome: the bookmark 'Telemundo' gets highlighted Actual outcome: 'Popular urls' gets highlighted and the submenu opens again.

I confirm this minor issue. I'd say it's another bug in the library. The item under the cursor should not be highlighted. I don't think it's a blocker for the branch merge though.

nmel updated this revision to Diff 31139.Apr 2 2018, 12:08 AM
  • Simplified if statements in KrBookmarkHandler::eventFilter
rade accepted this revision.EditedApr 2 2018, 1:02 AM

First off I'm marking the revision as accepted as either you agree with my points below and apply the patch which obviously I would agree with, or you don't agree and keep it as is which is also fine.
Either way this is a really good patch.
Thanks again for figuring out how to fix this issue!

It will be nice if someone reports all these bugs to Qt. If you have time, please do this.

I have already developed patches for the setActiveaction activating menus and the popup menu bug. I'll try to submit them to qt this once this feature is complete and merged as I don't really have the time to do both at once.

No race condition happens.

I skimmed the links you provided they look very interesting, I'll read them fully later to get a better understanding of how it works. For now I'll trust you that there isn't a race condition.

Secondly I don't like how _quicksearchMenu get's assigned values all over the place. This makes needlessly difficult too follow the logic of the code as all the if statements ....

{F5780757}

There is a logic here, it's not that I put it all over the place to make it just work. _quicksearchMenu is assigned in 2 cases:

I didn't mean to imply that there wasn't logic to their placement. I just feel that the code is less understandable for someone who is reading through the code for the first time. With my patch I feel that it is clearer for a first time reader what is happening. E.g when seeing

if (_quickSearchText().length() != 0)

It is self evident that this is looking at whether we have searched for anything or not.
But too understand when

if (_quickSearchMenu)

is run. You need to look through the entire function (technically the entire class!) and find all places where _quicksearchMenu is modified and understand why they are set when they are. Miss even one and you'll get a false understanding of when the if will be run.
Figuring out things like if there is there a reason the reset code is run when we receive a Show event followed by an immediate Close event, or whether that is just a side effect of the fact that we want quicksSearchMenu to be null when ever a submenu is closed so that we don't run the code when the user uses the arrow keys in the parent menu etc.

Your patch reduces number of lines but the logic will be lost.

The logic is different yes, but I fail to see how it does not cover all cases, save the case of where a menu was opened but we never searched in it. In such a case we don't need to reset on close anyway. Could you explain further?

This revision is now accepted and ready to land.Apr 2 2018, 1:02 AM
nmel added a comment.Apr 2 2018, 7:07 AM

To be clear, you patch should be working fine, this discussion is more about a taste. I prefer keeping the _quickSearchMenu as meaningful as possible. If we search or about to search in a menu, the var is tracking this menu, however when the menu is closed, we mark that we suspended tracking by setting the var to zero.

Checking if (_quickSearchMenu) is needed as we access memory by the _quickSearchMenu pointer. If we replace the condition with if (_quickSearchText().length() != 0) the first-time reader need to go through the entire code too and ensure that _quickSearchMenu != 0 only if _quickSearchText().length() != 0. Plus, later we may change some other logic and it will no longer be the case.

I appreciate that you take time to explain your way of thinking in detail. Discussions like this lead to a better code. I will wait for Martin or other devs to express their opinions. If I face no objection, I'll merge the current patch.

martinkostolny accepted this revision.Apr 2 2018, 10:47 AM

Thank you both for the effort to fix this issue! I'm accepting since this patch fixes the issue flawlessly and is readable for me. Rade's readability code suggestion is also OK but I really cannot decide which one is better, sorry :).

I have a minor code suggestions that are not important, so they doesn't need to be done.

krusader/BookMan/krbookmarkhandler.cpp
576

Same here - return statement:

return QObject::eventFilter(obj, ev);
577

How about placing this line at the end of if statement:

return QObject::eventFilter(obj, ev);

IMO it would improve readability and also it would be a minor optimization in some cases.

585

I'd place else to one line with previous closing bracket

This revision was automatically updated to reflect the committed changes.
nmel marked 3 inline comments as done.Apr 3 2018, 4:31 AM

Thanks Martin! I've incorporated your suggestions.