This adds a new Milou.ResultsModel for runner results which tries very hard to avoid model resets and instead signals addition, removal, and changes in results. One of the main advantages is that now the highlighted item isn't reset every time the results change, i.e. results that come in delayed won't unexpectedly change the keyboard selection.
The various result processing, filtering, and grouping features are split into tiny dedicated proxy models to make the code easier to follow and maintain. The QML API isn't changed so that look and feel themes don't need to be adjusted.
The raw results model is a tree of runner results grouped by matchCategory (e.g. "Documents", "Applications").
Results are then sorted by result type (ExactMatch, HelperMatch, etc) and by their relevance. The highest scored match in a group determines its overall score. The previous heuristic of prefering categories who have results with the query in their visible text has been removed but could easily be added back if deemed beneficial for the quality of results.
The groups are then limited in size with higher scored categories being allowed to show more matches. This model could in the future also be extended to allow expanding groups to show all of the results inside.
After that the model is flattened back into a tree with the group items removed for consumption in the existing ListView. Items with an identical text will have their subtext displayed automatically for disambiguation.