Merge branch 'dev' into Shortcut

This commit is contained in:
VictoriousRaptor 2022-11-08 12:41:44 +08:00 committed by GitHub
commit 4a6e298549
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 29 deletions

View file

@ -202,7 +202,11 @@ namespace Flow.Launcher.Infrastructure
if (allQuerySubstringsMatched)
{
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
// firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char
// preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6
// giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space.
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices,
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
@ -296,7 +300,7 @@ namespace Flow.Launcher.Infrastructure
return currentQuerySubstringIndex >= querySubstringsLength;
}
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List<int> spaceIndices, int matchLen,
bool allSubstringsContainedInCompareString)
{
// A match found near the beginning of a string is scored more than a match found near the end
@ -304,6 +308,14 @@ namespace Flow.Launcher.Infrastructure
// while the score is lower if they are more spread out
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
// Give more weight to a match that is closer to the start of the string.
// if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world'
// and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated,
// 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'
// will score slightly higher than 'hello world' because 'hello world' has one additional space.
if (firstIndex == 0 && allSubstringsContainedInCompareString)
score -= spaceIndices.Count;
// A match with less characters assigning more weights
if (stringToCompare.Length - query.Length < 5)
{

View file

@ -37,7 +37,11 @@ namespace Flow.Launcher.Plugin
/// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path
/// flow will copy the actual file/folder instead of just the path text.
/// </summary>
public string CopyText { get; set; } = string.Empty;
public string CopyText
{
get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText;
set => _copyText = value;
}
/// <summary>
/// This holds the text which can be provided by plugin to help Flow autocomplete text
@ -81,6 +85,7 @@ namespace Flow.Launcher.Plugin
/// Delegate to Get Image Source
/// </summary>
public IconDelegate Icon;
private string _copyText = string.Empty;
/// <summary>
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)

View file

@ -129,14 +129,20 @@ namespace Flow.Launcher.Test
}
}
/// <summary>
/// These are standard match scenarios
/// The intention of this test is provide a bench mark for how much the score has increased from a change.
/// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable.
/// </summary>
[TestCase(Chrome, Chrome, 157)]
[TestCase(Chrome, LastIsChrome, 147)]
[TestCase(Chrome, LastIsChrome, 145)]
[TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)]
[TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)]
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
[TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended
[TestCase("sql", MicrosoftSqlServerManagementStudio, 109)]
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
string queryString, string compareString, int expectedScore)
{
@ -275,7 +281,40 @@ namespace Flow.Launcher.Test
$"Query: \"{queryString}\"{Environment.NewLine} " +
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
$"Should be greater than{Environment.NewLine}" +
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
}
[TestCase("red", "red colour", "metro red")]
[TestCase("red", "this red colour", "this colour red")]
[TestCase("red", "this red colour", "this colour is very red")]
[TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")]
[TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")]
public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero(
string queryString, string compareString1, string compareString2)
{
// When
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
// Given
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
var compareString2Result = matcher.FuzzyMatch(queryString, compareString2);
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
Debug.WriteLine(
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
Debug.WriteLine(
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
Debug.WriteLine("###############################################");
Debug.WriteLine("");
// Should
Assert.True(compareString1Result.Score > compareString2Result.Score,
$"Query: \"{queryString}\"{Environment.NewLine} " +
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
$"Should be greater than{Environment.NewLine}" +
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
}
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]

View file

@ -300,7 +300,7 @@
<ContentControl>
<flowlauncher:ResultListBox x:Name="ResultListBox"
DataContext="{Binding Results}"
PreviewMouseDown="OnPreviewMouseButtonDown" />
PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" />
</ContentControl>
</Border>
<Border Style="{DynamicResource WindowRadius}">

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
@ -20,6 +20,11 @@ using Flow.Launcher.Infrastructure;
using System.Windows.Media;
using Flow.Launcher.Infrastructure.Hotkey;
using Flow.Launcher.Plugin.SharedCommands;
using System.Text;
using DataObject = System.Windows.DataObject;
using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Windows.Threading;
using System.Windows.Data;

View file

@ -25,12 +25,15 @@
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Standard"
Visibility="{Binding Visbility}"
mc:Ignorable="d">
mc:Ignorable="d"
PreviewMouseMove="ResultList_MouseMove"
PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown">
<!-- IsSynchronizedWithCurrentItem: http://stackoverflow.com/a/7833798/2833083 -->
<ListBox.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Stretch">
<Button
HorizontalAlignment="Stretch">
<Button.Template>
<ControlTemplate>
<ContentPresenter Content="{TemplateBinding Button.Content}" />
@ -84,7 +87,7 @@
x:Name="ImageIcon"
Width="{Binding IconXY}"
Height="{Binding IconXY}"
Margin="0,0,0,0"
Margin="0,0,0,0" IsHitTestVisible="False"
HorizontalAlignment="Center"
Source="{Binding Image, TargetNullValue={x:Null}}"
Stretch="Uniform"
@ -134,6 +137,7 @@
x:Name="Title"
VerticalAlignment="Center"
DockPanel.Dock="Left"
IsHitTestVisible="False"
Style="{DynamicResource ItemTitleStyle}"
Text="{Binding Result.Title}"
ToolTip="{Binding ShowTitleToolTip}">
@ -147,6 +151,7 @@
<TextBlock
x:Name="SubTitle"
Grid.Row="1"
IsHitTestVisible="False"
Style="{DynamicResource ItemSubTitleStyle}"
Text="{Binding Result.SubTitle}"
ToolTip="{Binding ShowSubTitleToolTip}" />

View file

@ -1,6 +1,9 @@
using System.Windows;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Flow.Launcher.ViewModel;
namespace Flow.Launcher
{
@ -24,7 +27,7 @@ namespace Flow.Launcher
private void OnMouseEnter(object sender, MouseEventArgs e)
{
lock(_lock)
lock (_lock)
{
curItem = (ListBoxItem)sender;
var p = e.GetPosition((IInputElement)sender);
@ -34,7 +37,7 @@ namespace Flow.Launcher
private void OnMouseMove(object sender, MouseEventArgs e)
{
lock(_lock)
lock (_lock)
{
var p = e.GetPosition((IInputElement)sender);
if (_lastpos != p)
@ -46,7 +49,7 @@ namespace Flow.Launcher
private void ListBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
lock(_lock)
lock (_lock)
{
if (curItem != null)
{
@ -54,5 +57,51 @@ namespace Flow.Launcher
}
}
}
private Point start;
private string path;
private string query;
private void ResultList_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.DirectlyOver is not FrameworkElement { DataContext: ResultViewModel result })
return;
path = result.Result.CopyText;
query = result.Result.OriginQuery.RawQuery;
start = e.GetPosition(null);
}
private void ResultList_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed)
{
start = default;
path = string.Empty;
query = string.Empty;
return;
}
if (!File.Exists(path) && !Directory.Exists(path))
return;
Point mousePosition = e.GetPosition(null);
Vector diff = this.start - mousePosition;
if (Math.Abs(diff.X) < SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) < SystemParameters.MinimumVerticalDragDistance)
return;
var data = new DataObject(DataFormats.FileDrop, new[]
{
path
});
DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Move | DragDropEffects.Copy);
App.API.ChangeQuery(query, true);
e.Handled = true;
}
}
}

View file

@ -1002,28 +1002,26 @@ namespace Flow.Launcher.ViewModel
var result = Results.SelectedItem?.Result;
if (result != null)
{
string copyText = string.IsNullOrEmpty(result.CopyText) ? result.SubTitle : result.CopyText;
string copyText = result.CopyText;
var isFile = File.Exists(copyText);
var isFolder = Directory.Exists(copyText);
if (isFile || isFolder)
{
var paths = new StringCollection();
paths.Add(copyText);
var paths = new StringCollection
{
copyText
};
Clipboard.SetFileDropList(paths);
App.API.ShowMsg(
App.API.GetTranslation("copy")
+ " "
+ (isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle")),
$"{App.API.GetTranslation("copy")} {(isFile ? App.API.GetTranslation("fileTitle") : App.API.GetTranslation("folderTitle"))}",
App.API.GetTranslation("completedSuccessfully"));
}
else
{
Clipboard.SetDataObject(copyText.ToString());
Clipboard.SetDataObject(copyText);
App.API.ShowMsg(
App.API.GetTranslation("copy")
+ " "
+ App.API.GetTranslation("textTitle"),
$"{App.API.GetTranslation("copy")} {App.API.GetTranslation("textTitle")}",
App.API.GetTranslation("completedSuccessfully"));
}
}

View file

@ -390,7 +390,6 @@ namespace Flow.Launcher.Plugin.Program.Programs
_ => Win32Program(x)
});
return programs;
}
@ -411,7 +410,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
ShortcutExtension => LnkProgram(x),
UrlExtension => UrlProgram(x),
_ => Win32Program(x)
}).Where(x => x.Valid);
});
return programs;
}
@ -569,7 +568,7 @@ namespace Flow.Launcher.Plugin.Program.Programs
autoIndexPrograms = ProgramsHasher(autoIndexPrograms);
return programs.Concat(autoIndexPrograms).Distinct().ToArray();
return programs.Concat(autoIndexPrograms).Where(x => x.Valid).Distinct().ToArray();
}
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
catch (Exception)