Skip to content

Commit 4a6e298

Browse files
Merge branch 'dev' into Shortcut
2 parents 5a0e432 + 588ef4b commit 4a6e298

File tree

9 files changed

+141
-29
lines changed

9 files changed

+141
-29
lines changed

Flow.Launcher.Infrastructure/StringMatcher.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,11 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
202202
if (allQuerySubstringsMatched)
203203
{
204204
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
205-
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
205+
206+
// firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char
207+
// preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6
208+
// giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space.
209+
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices,
206210
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
207211

208212
var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
@@ -296,14 +300,22 @@ private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, in
296300
return currentQuerySubstringIndex >= querySubstringsLength;
297301
}
298302

299-
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
303+
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List<int> spaceIndices, int matchLen,
300304
bool allSubstringsContainedInCompareString)
301305
{
302306
// A match found near the beginning of a string is scored more than a match found near the end
303307
// A match is scored more if the characters in the patterns are closer to each other,
304308
// while the score is lower if they are more spread out
305309
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
306310

311+
// Give more weight to a match that is closer to the start of the string.
312+
// if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world'
313+
// and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated,
314+
// to prevent them scoring the same, we adjust the score by deducting the number of spaces it has from the start of the string, so 'world hello'
315+
// will score slightly higher than 'hello world' because 'hello world' has one additional space.
316+
if (firstIndex == 0 && allSubstringsContainedInCompareString)
317+
score -= spaceIndices.Count;
318+
307319
// A match with less characters assigning more weights
308320
if (stringToCompare.Length - query.Length < 5)
309321
{

Flow.Launcher.Plugin/Result.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ public class Result
3737
/// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path
3838
/// flow will copy the actual file/folder instead of just the path text.
3939
/// </summary>
40-
public string CopyText { get; set; } = string.Empty;
40+
public string CopyText
41+
{
42+
get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText;
43+
set => _copyText = value;
44+
}
4145

4246
/// <summary>
4347
/// This holds the text which can be provided by plugin to help Flow autocomplete text
@@ -81,6 +85,7 @@ public string IcoPath
8185
/// Delegate to Get Image Source
8286
/// </summary>
8387
public IconDelegate Icon;
88+
private string _copyText = string.Empty;
8489

8590
/// <summary>
8691
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)

Flow.Launcher.Test/FuzzyMatcherTest.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,20 @@ public void GivenQueryString_WhenAppliedPrecisionFiltering_ThenShouldReturnGreat
129129
}
130130
}
131131

132+
133+
/// <summary>
134+
/// These are standard match scenarios
135+
/// The intention of this test is provide a bench mark for how much the score has increased from a change.
136+
/// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable.
137+
/// </summary>
132138
[TestCase(Chrome, Chrome, 157)]
133-
[TestCase(Chrome, LastIsChrome, 147)]
139+
[TestCase(Chrome, LastIsChrome, 145)]
134140
[TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)]
135141
[TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)]
136142
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
137143
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
138-
[TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
139-
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended
144+
[TestCase("sql", MicrosoftSqlServerManagementStudio, 109)]
145+
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended
140146
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
141147
string queryString, string compareString, int expectedScore)
142148
{
@@ -275,7 +281,40 @@ public void WhenGivenAQuery_Scoring_ShouldGiveMoreWeightToStartOfNewWord(
275281
$"Query: \"{queryString}\"{Environment.NewLine} " +
276282
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
277283
$"Should be greater than{Environment.NewLine}" +
278-
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
284+
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
285+
}
286+
287+
[TestCase("red", "red colour", "metro red")]
288+
[TestCase("red", "this red colour", "this colour red")]
289+
[TestCase("red", "this red colour", "this colour is very red")]
290+
[TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")]
291+
[TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")]
292+
public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero(
293+
string queryString, string compareString1, string compareString2)
294+
{
295+
// When
296+
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
297+
298+
// Given
299+
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
300+
var compareString2Result = matcher.FuzzyMatch(queryString, compareString2);
301+
302+
Debug.WriteLine("");
303+
Debug.WriteLine("###############################################");
304+
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
305+
Debug.WriteLine(
306+
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
307+
Debug.WriteLine(
308+
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
309+
Debug.WriteLine("###############################################");
310+
Debug.WriteLine("");
311+
312+
// Should
313+
Assert.True(compareString1Result.Score > compareString2Result.Score,
314+
$"Query: \"{queryString}\"{Environment.NewLine} " +
315+
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
316+
$"Should be greater than{Environment.NewLine}" +
317+
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
279318
}
280319

281320
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]

Flow.Launcher/MainWindow.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@
300300
<ContentControl>
301301
<flowlauncher:ResultListBox x:Name="ResultListBox"
302302
DataContext="{Binding Results}"
303-
PreviewMouseDown="OnPreviewMouseButtonDown" />
303+
PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" />
304304
</ContentControl>
305305
</Border>
306306
<Border Style="{DynamicResource WindowRadius}">

Flow.Launcher/MainWindow.xaml.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.ComponentModel;
33
using System.Threading.Tasks;
44
using System.Windows;
@@ -20,6 +20,11 @@
2020
using System.Windows.Media;
2121
using Flow.Launcher.Infrastructure.Hotkey;
2222
using Flow.Launcher.Plugin.SharedCommands;
23+
using System.Text;
24+
using DataObject = System.Windows.DataObject;
25+
using System.Diagnostics;
26+
using Microsoft.AspNetCore.Http;
27+
using System.IO;
2328
using System.Windows.Threading;
2429
using System.Windows.Data;
2530

Flow.Launcher/ResultListBox.xaml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@
2525
VirtualizingStackPanel.IsVirtualizing="True"
2626
VirtualizingStackPanel.VirtualizationMode="Standard"
2727
Visibility="{Binding Visbility}"
28-
mc:Ignorable="d">
28+
mc:Ignorable="d"
29+
PreviewMouseMove="ResultList_MouseMove"
30+
PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown">
2931
<!-- IsSynchronizedWithCurrentItem: http://stackoverflow.com/a/7833798/2833083 -->
3032

3133
<ListBox.ItemTemplate>
3234
<DataTemplate>
33-
<Button HorizontalAlignment="Stretch">
35+
<Button
36+
HorizontalAlignment="Stretch">
3437
<Button.Template>
3538
<ControlTemplate>
3639
<ContentPresenter Content="{TemplateBinding Button.Content}" />
@@ -84,7 +87,7 @@
8487
x:Name="ImageIcon"
8588
Width="{Binding IconXY}"
8689
Height="{Binding IconXY}"
87-
Margin="0,0,0,0"
90+
Margin="0,0,0,0" IsHitTestVisible="False"
8891
HorizontalAlignment="Center"
8992
Source="{Binding Image, TargetNullValue={x:Null}}"
9093
Stretch="Uniform"
@@ -134,6 +137,7 @@
134137
x:Name="Title"
135138
VerticalAlignment="Center"
136139
DockPanel.Dock="Left"
140+
IsHitTestVisible="False"
137141
Style="{DynamicResource ItemTitleStyle}"
138142
Text="{Binding Result.Title}"
139143
ToolTip="{Binding ShowTitleToolTip}">
@@ -147,6 +151,7 @@
147151
<TextBlock
148152
x:Name="SubTitle"
149153
Grid.Row="1"
154+
IsHitTestVisible="False"
150155
Style="{DynamicResource ItemSubTitleStyle}"
151156
Text="{Binding Result.SubTitle}"
152157
ToolTip="{Binding ShowSubTitleToolTip}" />

Flow.Launcher/ResultListBox.xaml.cs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
using System.Windows;
1+
using System;
2+
using System.IO;
3+
using System.Windows;
24
using System.Windows.Controls;
35
using System.Windows.Input;
6+
using Flow.Launcher.ViewModel;
47

58
namespace Flow.Launcher
69
{
@@ -24,7 +27,7 @@ private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
2427

2528
private void OnMouseEnter(object sender, MouseEventArgs e)
2629
{
27-
lock(_lock)
30+
lock (_lock)
2831
{
2932
curItem = (ListBoxItem)sender;
3033
var p = e.GetPosition((IInputElement)sender);
@@ -34,7 +37,7 @@ private void OnMouseEnter(object sender, MouseEventArgs e)
3437

3538
private void OnMouseMove(object sender, MouseEventArgs e)
3639
{
37-
lock(_lock)
40+
lock (_lock)
3841
{
3942
var p = e.GetPosition((IInputElement)sender);
4043
if (_lastpos != p)
@@ -46,13 +49,59 @@ private void OnMouseMove(object sender, MouseEventArgs e)
4649

4750
private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
4851
{
49-
lock(_lock)
52+
lock (_lock)
5053
{
5154
if (curItem != null)
5255
{
5356
curItem.IsSelected = true;
5457
}
5558
}
5659
}
60+
61+
62+
private Point start;
63+
private string path;
64+
private string query;
65+
66+
private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
67+
{
68+
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
69+
return;
70+
71+
path = result.Result.CopyText;
72+
query = result.Result.OriginQuery.RawQuery;
73+
start = e.GetPosition(null);
74+
}
75+
76+
private void ResultList_MouseMove(object sender, MouseEventArgs e)
77+
{
78+
if (e.LeftButton != MouseButtonState.Pressed)
79+
{
80+
start = default;
81+
path = string.Empty;
82+
query = string.Empty;
83+
return;
84+
}
85+
86+
if (!File.Exists(path) && !Directory.Exists(path))
87+
return;
88+
89+
Point mousePosition = e.GetPosition(null);
90+
Vector diff = this.start - mousePosition;
91+
92+
if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
93+
|| Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
94+
return;
95+
96+
var data = new DataObject(DataFormats.FileDrop, new[]
97+
{
98+
path
99+
});
100+
DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
101+
102+
App.API.ChangeQuery(query, true);
103+
104+
e.Handled = true;
105+
}
57106
}
58107
}

Flow.Launcher/ViewModel/MainViewModel.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,28 +1002,26 @@ public void ResultCopy(string stringToCopy)
10021002
var result = Results.SelectedItem?.Result;
10031003
if (result != null)
10041004
{
1005-
string copyText = string.IsNullOrEmpty(result.CopyText) ? result.SubTitle : result.CopyText;
1005+
string copyText = result.CopyText;
10061006
var isFile = File.Exists(copyText);
10071007
var isFolder = Directory.Exists(copyText);
10081008
if (isFile || isFolder)
10091009
{
1010-
var paths = new StringCollection();
1011-
paths.Add(copyText);
1010+
var paths = new StringCollection
1011+
{
1012+
copyText
1013+
};
10121014

10131015
Clipboard.SetFileDropList(paths);
10141016
App.API.ShowMsg(
1015-
App.API.GetTranslation("copy")
1016-
+ " "
1017-
+ (isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle")),
1017+
$"{App.API.GetTranslation("copy")} {(isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle"))}",
10181018
App.API.GetTranslation("completedSuccessfully"));
10191019
}
10201020
else
10211021
{
1022-
Clipboard.SetDataObject(copyText.ToString());
1022+
Clipboard.SetDataObject(copyText);
10231023
App.API.ShowMsg(
1024-
App.API.GetTranslation("copy")
1025-
+ " "
1026-
+ App.API.GetTranslation("textTitle"),
1024+
$"{App.API.GetTranslation("copy")} {App.API.GetTranslation("textTitle")}",
10271025
App.API.GetTranslation("completedSuccessfully"));
10281026
}
10291027
}

Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,6 @@ private static IEnumerable<Win32> UnregisteredPrograms(List<Settings.ProgramSour
390390
_ => Win32Program(x)
391391
});
392392

393-
394393
return programs;
395394
}
396395

@@ -411,7 +410,7 @@ private static IEnumerable<Win32> StartMenuPrograms(string[] suffixes)
411410
ShortcutExtension => LnkProgram(x),
412411
UrlExtension => UrlProgram(x),
413412
_ => Win32Program(x)
414-
}).Where(x => x.Valid);
413+
});
415414
return programs;
416415
}
417416

@@ -569,7 +568,7 @@ public static Win32[] All(Settings settings)
569568

570569
autoIndexPrograms = ProgramsHasher(autoIndexPrograms);
571570

572-
return programs.Concat(autoIndexPrograms).Distinct().ToArray();
571+
return programs.Concat(autoIndexPrograms).Where(x => x.Valid).Distinct().ToArray();
573572
}
574573
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
575574
catch (Exception)

0 commit comments

Comments
 (0)