diff --git a/Doc/app.ico b/Doc/app.ico
index 38c401c8c..362c8747f 100644
Binary files a/Doc/app.ico and b/Doc/app.ico differ
diff --git a/Doc/app.png b/Doc/app.png
index 8c9ca7971..ba17c1a66 100644
Binary files a/Doc/app.png and b/Doc/app.png differ
diff --git a/Doc/app.psd b/Doc/app.psd
deleted file mode 100644
index 833fd6529..000000000
Binary files a/Doc/app.psd and /dev/null differ
diff --git a/Doc/app_error.png b/Doc/app_error.png
index 5106d6e8a..4c035da0c 100644
Binary files a/Doc/app_error.png and b/Doc/app_error.png differ
diff --git a/Doc/app_error.psd b/Doc/app_error.psd
deleted file mode 100644
index 174e9cb62..000000000
Binary files a/Doc/app_error.psd and /dev/null differ
diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
index d5fc55da8..fa3f10fa7 100644
--- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj
+++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
@@ -11,6 +11,7 @@
false
false
false
+ en
@@ -52,10 +53,11 @@
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs
index 31e1c7032..35486e794 100644
--- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs
+++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs
@@ -20,21 +20,21 @@ namespace Flow.Launcher.Core.Plugin
public static List Plugins(List metadatas, PluginsSettings settings)
{
- var csharpPlugins = CSharpPlugins(metadatas).ToList();
+ var dotnetPlugins = DotNetPlugins(metadatas).ToList();
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
var executablePlugins = ExecutablePlugins(metadatas);
- var plugins = csharpPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
+ var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
return plugins;
}
- public static IEnumerable CSharpPlugins(List source)
+ public static IEnumerable DotNetPlugins(List source)
{
var plugins = new List();
- var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp);
+ var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language));
foreach (var metadata in metadatas)
{
- var milliseconds = Stopwatch.Debug($"|PluginsLoader.CSharpPlugins|Constructor init cost for {metadata.Name}", () =>
+ var milliseconds = Stopwatch.Debug($"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
{
#if DEBUG
@@ -50,7 +50,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
- Log.Exception($"|PluginsLoader.CSharpPlugins|Couldn't load assembly for {metadata.Name}", e);
+ Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e);
return;
}
var types = assembly.GetTypes();
@@ -61,7 +61,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (InvalidOperationException e)
{
- Log.Exception($"|PluginsLoader.CSharpPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e);
+ Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e);
return;
}
IPlugin plugin;
@@ -71,7 +71,7 @@ namespace Flow.Launcher.Core.Plugin
}
catch (Exception e)
{
- Log.Exception($"|PluginsLoader.CSharpPlugins|Can't create instance for <{metadata.Name}>", e);
+ Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e);
return;
}
#endif
diff --git a/Flow.Launcher.CrashReporter/Images/app_error.png b/Flow.Launcher.CrashReporter/Images/app_error.png
index 5106d6e8a..4c035da0c 100644
Binary files a/Flow.Launcher.CrashReporter/Images/app_error.png and b/Flow.Launcher.CrashReporter/Images/app_error.png differ
diff --git a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
index 873eb306e..ed3a6584d 100644
--- a/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ImageLoader.cs
@@ -13,11 +13,12 @@ namespace Flow.Launcher.Infrastructure.Image
{
public static class ImageLoader
{
- private static readonly ImageCache ImageCache = new ImageCache();
- private static BinaryStorage> _storage;
- private static readonly ConcurrentDictionary GuidToKey = new ConcurrentDictionary();
- private static IImageHashGenerator _hashGenerator;
+ private static readonly ImageCache _imageCache = new ImageCache();
+ private static readonly ConcurrentDictionary _guidToKey = new ConcurrentDictionary();
+ private static readonly bool _enableHashImage = true;
+ private static BinaryStorage> _storage;
+ private static IImageHashGenerator _hashGenerator;
private static readonly string[] ImageExtensions =
{
@@ -30,30 +31,30 @@ namespace Flow.Launcher.Infrastructure.Image
".ico"
};
-
public static void Initialize()
{
_storage = new BinaryStorage>("Image");
_hashGenerator = new ImageHashGenerator();
- ImageCache.Usage = LoadStorageToConcurrentDictionary();
+ _imageCache.Usage = LoadStorageToConcurrentDictionary();
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
{
ImageSource img = new BitmapImage(new Uri(icon));
img.Freeze();
- ImageCache[icon] = img;
+ _imageCache[icon] = img;
}
+
Task.Run(() =>
{
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
{
- ImageCache.Usage.AsParallel().ForAll(x =>
+ _imageCache.Usage.AsParallel().ForAll(x =>
{
Load(x.Key);
});
});
- Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
+ Log.Info($"|ImageLoader.Initialize|Number of preload images is <{_imageCache.Usage.Count}>, Images Number: {_imageCache.CacheSize()}, Unique Items {_imageCache.UniqueImagesInCache()}");
});
}
@@ -61,7 +62,7 @@ namespace Flow.Launcher.Infrastructure.Image
{
lock (_storage)
{
- _storage.Save(ImageCache.CleanupAndToDictionary());
+ _storage.Save(_imageCache.CleanupAndToDictionary());
}
}
@@ -99,17 +100,17 @@ namespace Flow.Launcher.Infrastructure.Image
private static ImageResult LoadInternal(string path, bool loadFullImage = false)
{
- ImageSource image;
- ImageType type = ImageType.Error;
+ ImageResult imageResult;
+
try
{
if (string.IsNullOrEmpty(path))
{
- return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
+ return new ImageResult(_imageCache[Constant.ErrorIcon], ImageType.Error);
}
- if (ImageCache.ContainsKey(path))
+ if (_imageCache.ContainsKey(path))
{
- return new ImageResult(ImageCache[path], ImageType.Cache);
+ return new ImageResult(_imageCache[path], ImageType.Cache);
}
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
@@ -124,69 +125,93 @@ namespace Flow.Launcher.Infrastructure.Image
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
}
- if (Directory.Exists(path))
+ imageResult = GetThumbnailResult(ref path, loadFullImage);
+ }
+ catch (System.Exception e)
+ {
+ try
{
- /* Directories can also have thumbnails instead of shell icons.
- * Generating thumbnails for a bunch of folders while scrolling through
- * results from Everything makes a big impact on performance and
- * Flow.Launcher responsibility.
- * - Solution: just load the icon
- */
- type = ImageType.Folder;
- image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
- Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
-
+ // Get thumbnail may fail for certain images on the first try, retry again has proven to work
+ imageResult = GetThumbnailResult(ref path, loadFullImage);
}
- else if (File.Exists(path))
+ catch (System.Exception e2)
{
- var extension = Path.GetExtension(path).ToLower();
- if (ImageExtensions.Contains(extension))
+ Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
+ Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
+
+ ImageSource image = _imageCache[Constant.ErrorIcon];
+ _imageCache[path] = image;
+ imageResult = new ImageResult(image, ImageType.Error);
+ }
+ }
+
+ return imageResult;
+ }
+
+ private static ImageResult GetThumbnailResult(ref string path, bool loadFullImage = false)
+ {
+ ImageSource image;
+ ImageType type = ImageType.Error;
+
+ if (Directory.Exists(path))
+ {
+ /* Directories can also have thumbnails instead of shell icons.
+ * Generating thumbnails for a bunch of folders while scrolling through
+ * results from Everything makes a big impact on performance and
+ * Flow.Launcher responsibility.
+ * - Solution: just load the icon
+ */
+ type = ImageType.Folder;
+ image = GetThumbnail(path, ThumbnailOptions.IconOnly);
+ }
+ else if (File.Exists(path))
+ {
+ var extension = Path.GetExtension(path).ToLower();
+ if (ImageExtensions.Contains(extension))
+ {
+ type = ImageType.ImageFile;
+ if (loadFullImage)
{
- type = ImageType.ImageFile;
- if (loadFullImage)
- {
- image = LoadFullImage(path);
- }
- else
- {
- /* Although the documentation for GetImage on MSDN indicates that
- * if a thumbnail is available it will return one, this has proved to not
- * be the case in many situations while testing.
- * - Solution: explicitly pass the ThumbnailOnly flag
- */
- image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
- Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly);
- }
+ image = LoadFullImage(path);
}
else
{
- type = ImageType.File;
- image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
- Constant.ThumbnailSize, ThumbnailOptions.None);
+ /* Although the documentation for GetImage on MSDN indicates that
+ * if a thumbnail is available it will return one, this has proved to not
+ * be the case in many situations while testing.
+ * - Solution: explicitly pass the ThumbnailOnly flag
+ */
+ image = GetThumbnail(path, ThumbnailOptions.ThumbnailOnly);
}
}
else
{
- image = ImageCache[Constant.ErrorIcon];
- path = Constant.ErrorIcon;
- }
-
- if (type != ImageType.Error)
- {
- image.Freeze();
+ type = ImageType.File;
+ image = GetThumbnail(path, ThumbnailOptions.None);
}
}
- catch (System.Exception e)
+ else
{
- Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
- type = ImageType.Error;
- image = ImageCache[Constant.ErrorIcon];
- ImageCache[path] = image;
+ image = _imageCache[Constant.ErrorIcon];
+ path = Constant.ErrorIcon;
}
+
+ if (type != ImageType.Error)
+ {
+ image.Freeze();
+ }
+
return new ImageResult(image, type);
}
- private static bool EnableImageHash = true;
+ private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly)
+ {
+ return WindowsThumbnailProvider.GetThumbnail(
+ path,
+ Constant.ThumbnailSize,
+ Constant.ThumbnailSize,
+ option);
+ }
public static ImageSource Load(string path, bool loadFullImage = false)
{
@@ -194,25 +219,27 @@ namespace Flow.Launcher.Infrastructure.Image
var img = imageResult.ImageSource;
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
- { // we need to get image hash
- string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
+ {
+ // we need to get image hash
+ string hash = _enableHashImage ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null)
{
- if (GuidToKey.TryGetValue(hash, out string key))
- { // image already exists
- img = ImageCache[key];
+ if (_guidToKey.TryGetValue(hash, out string key))
+ {
+ // image already exists
+ img = _imageCache[key];
}
else
- { // new guid
- GuidToKey[hash] = path;
+ {
+ // new guid
+ _guidToKey[hash] = path;
}
}
// update cache
- ImageCache[path] = img;
+ _imageCache[path] = img;
}
-
return img;
}
diff --git a/Flow.Launcher.Infrastructure/KeyConstant.cs b/Flow.Launcher.Infrastructure/KeyConstant.cs
new file mode 100644
index 000000000..317485176
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/KeyConstant.cs
@@ -0,0 +1,9 @@
+namespace Flow.Launcher.Infrastructure
+{
+ public static class KeyConstant
+ {
+ public const string Ctrl = nameof(Ctrl);
+ public const string Alt = nameof(Alt);
+ public const string Space = nameof(Space);
+ }
+}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index dbb47fd8e..84b0a7990 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -9,7 +9,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
{
public class Settings : BaseModel
{
- public string Hotkey { get; set; } = "Alt + Space";
+ public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
+ public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
+ public bool ShowOpenResultHotkey { get; set; } = true;
public string Language { get; set; } = "en";
public string Theme { get; set; } = "Dark";
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
diff --git a/Flow.Launcher.Plugin/AllowedLanguage.cs b/Flow.Launcher.Plugin/AllowedLanguage.cs
index 9f63c09fc..827958a7b 100644
--- a/Flow.Launcher.Plugin/AllowedLanguage.cs
+++ b/Flow.Launcher.Plugin/AllowedLanguage.cs
@@ -12,15 +12,26 @@
get { return "CSHARP"; }
}
+ public static string FSharp
+ {
+ get { return "FSHARP"; }
+ }
+
public static string Executable
{
get { return "EXECUTABLE"; }
}
+ public static bool IsDotNet(string language)
+ {
+ return language.ToUpper() == CSharp
+ || language.ToUpper() == FSharp;
+ }
+
public static bool IsAllowed(string language)
{
- return language.ToUpper() == Python.ToUpper()
- || language.ToUpper() == CSharp.ToUpper()
+ return IsDotNet(language)
+ || language.ToUpper() == Python.ToUpper()
|| language.ToUpper() == Executable.ToUpper();
}
}
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 0c6df2b59..82fbb31d5 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -1,4 +1,4 @@
-
+
netcoreapp3.1
@@ -55,7 +55,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
index 2f54813e1..2dbf1a9d3 100644
--- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj
+++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj
@@ -10,6 +10,7 @@
false
+ en
@@ -46,10 +47,13 @@
-
+
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/Flow.Launcher.sln b/Flow.Launcher.sln
index 9f1ad9499..2827cf585 100644
--- a/Flow.Launcher.sln
+++ b/Flow.Launcher.sln
@@ -74,6 +74,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Browse
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Calculator", "Plugins\Flow.Launcher.Plugin.Calculator\Flow.Launcher.Plugin.Calculator.csproj", "{59BD9891-3837-438A-958D-ADC7F91F6F7E}"
EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "HelloWorldFSharp", "Plugins\HelloWorldFSharp\HelloWorldFSharp.fsproj", "{30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -313,6 +315,18 @@ Global
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|Any CPU
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.ActiveCfg = Release|Any CPU
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.Build.0 = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x64.Build.0 = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Debug|x86.Build.0 = Debug|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x64.ActiveCfg = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x64.Build.0 = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.ActiveCfg = Release|Any CPU
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -332,6 +346,7 @@ Global
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
+ {30DDA7D9-3712-44F4-BD18-DC1C05B2DD9E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
diff --git a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs
new file mode 100644
index 000000000..7de5af79a
--- /dev/null
+++ b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Flow.Launcher.Converters
+{
+ [ValueConversion(typeof(bool), typeof(Visibility))]
+ public class OpenResultHotkeyVisibilityConverter : IValueConverter
+ {
+ private const int MaxVisibleHotkeys = 9;
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var hotkeyNumber = int.MaxValue;
+
+ if (value is ListBoxItem listBoxItem)
+ {
+ ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
+ hotkeyNumber = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
+ }
+
+ return hotkeyNumber <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
+ }
+}
diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs
new file mode 100644
index 000000000..970ed183c
--- /dev/null
+++ b/Flow.Launcher/Converters/OrdinalConverter.cs
@@ -0,0 +1,22 @@
+using System.Globalization;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Flow.Launcher.Converters
+{
+ public class OrdinalConverter : IValueConverter
+ {
+ public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is ListBoxItem listBoxItem)
+ {
+ ListBox listBox = ItemsControl.ItemsControlFromItemContainer(listBoxItem) as ListBox;
+ return listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
+ }
+
+ return 0;
+ }
+
+ public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
+ }
+}
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index 4baeb69e4..7049d9c4d 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
@@ -11,6 +11,7 @@
false
false
false
+ en
@@ -64,7 +65,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Flow.Launcher/Images/app.png b/Flow.Launcher/Images/app.png
index 8c9ca7971..ba17c1a66 100644
Binary files a/Flow.Launcher/Images/app.png and b/Flow.Launcher/Images/app.png differ
diff --git a/Flow.Launcher/Images/app_error.png b/Flow.Launcher/Images/app_error.png
index 5106d6e8a..4c035da0c 100644
Binary files a/Flow.Launcher/Images/app_error.png and b/Flow.Launcher/Images/app_error.png differ
diff --git a/Flow.Launcher/Languages/da.xaml b/Flow.Launcher/Languages/da.xaml
index 4c3d4bd85..e8ec05ef9 100644
--- a/Flow.Launcher/Languages/da.xaml
+++ b/Flow.Launcher/Languages/da.xaml
@@ -51,7 +51,9 @@
Genvejstast
Flow Launcher genvejstast
+ Åbn resultatmodifikatorer
Tilpasset søgegenvejstast
+ Vis hotkey
Slet
Rediger
Tilføj
diff --git a/Flow.Launcher/Languages/de.xaml b/Flow.Launcher/Languages/de.xaml
index d1eae3124..c2e77cc47 100644
--- a/Flow.Launcher/Languages/de.xaml
+++ b/Flow.Launcher/Languages/de.xaml
@@ -51,7 +51,9 @@
Tastenkombination
Flow Launcher Tastenkombination
+ Öffnen Sie die Ergebnismodifikatoren
Benutzerdefinierte Abfrage Tastenkombination
+ Hotkey anzeigen
Löschen
Bearbeiten
Hinzufügen
diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml
index 46519f058..c33093cab 100644
--- a/Flow.Launcher/Languages/en.xaml
+++ b/Flow.Launcher/Languages/en.xaml
@@ -60,6 +60,8 @@
Hotkey
Flow Launcher Hotkey
+ Open Result Modifiers
+ Show Hotkey
Custom Query Hotkey
Delete
Edit
diff --git a/Flow.Launcher/Languages/fr.xaml b/Flow.Launcher/Languages/fr.xaml
index 4ceb06249..753ccfdfa 100644
--- a/Flow.Launcher/Languages/fr.xaml
+++ b/Flow.Launcher/Languages/fr.xaml
@@ -55,7 +55,9 @@
Raccourcis
Ouvrir Flow Launcher
+ Modificateurs de résultats ouverts
Requêtes personnalisées
+ Afficher le raccourci clavier
Supprimer
Modifier
Ajouter
diff --git a/Flow.Launcher/Languages/it.xaml b/Flow.Launcher/Languages/it.xaml
index 381864f8a..f01b96bcc 100644
--- a/Flow.Launcher/Languages/it.xaml
+++ b/Flow.Launcher/Languages/it.xaml
@@ -55,7 +55,9 @@
Tasti scelta rapida
Tasto scelta rapida Flow Launcher
+ Apri modificatori di risultato
Tasti scelta rapida per ricerche personalizzate
+ Mostra tasto di scelta rapida
Cancella
Modifica
Aggiungi
diff --git a/Flow.Launcher/Languages/ja.xaml b/Flow.Launcher/Languages/ja.xaml
index 0fd5e3e79..c6cabdce9 100644
--- a/Flow.Launcher/Languages/ja.xaml
+++ b/Flow.Launcher/Languages/ja.xaml
@@ -58,7 +58,9 @@
ホットキー
Flow Launcher ホットキー
+ 結果修飾子を開く
カスタムクエリ ホットキー
+ ホットキーを表示
削除
編集
追加
diff --git a/Flow.Launcher/Languages/ko.xaml b/Flow.Launcher/Languages/ko.xaml
index 2f7d538dc..c52e1cb23 100644
--- a/Flow.Launcher/Languages/ko.xaml
+++ b/Flow.Launcher/Languages/ko.xaml
@@ -55,7 +55,9 @@
핫키
Flow Launcher 핫키
+ 결과 수정 자 열기
사용자지정 쿼리 핫키
+ 단축키 표시
삭제
편집
추가
diff --git a/Flow.Launcher/Languages/nb-NO.xaml b/Flow.Launcher/Languages/nb-NO.xaml
index 035b0cbdf..453203b4d 100644
--- a/Flow.Launcher/Languages/nb-NO.xaml
+++ b/Flow.Launcher/Languages/nb-NO.xaml
@@ -55,7 +55,9 @@
Hurtigtast
Flow Launcher-hurtigtast
+ Åpne resultatmodifiserere
Egendefinerd spørringshurtigtast
+ Vis hurtigtast
Slett
Rediger
Legg til
diff --git a/Flow.Launcher/Languages/nl.xaml b/Flow.Launcher/Languages/nl.xaml
index be18beb9c..492ea5a8c 100644
--- a/Flow.Launcher/Languages/nl.xaml
+++ b/Flow.Launcher/Languages/nl.xaml
@@ -51,7 +51,9 @@
Sneltoets
Flow Launcher Sneltoets
+ Open resultaatmodificatoren
Custom Query Sneltoets
+ Sneltoets weergeven
Verwijder
Bewerken
Toevoegen
diff --git a/Flow.Launcher/Languages/pl.xaml b/Flow.Launcher/Languages/pl.xaml
index 249878ed3..95fa57bd2 100644
--- a/Flow.Launcher/Languages/pl.xaml
+++ b/Flow.Launcher/Languages/pl.xaml
@@ -51,7 +51,9 @@
Skrót klawiszowy
Skrót klawiszowy Flow Launcher
+ Modyfikatory klawiszów otwierających wyniki
Skrót klawiszowy niestandardowych zapytań
+ Pokaż skrót klawiszowy
Usuń
Edytuj
Dodaj
diff --git a/Flow.Launcher/Languages/pt-br.xaml b/Flow.Launcher/Languages/pt-br.xaml
index 2036744e8..d5d2bf76d 100644
--- a/Flow.Launcher/Languages/pt-br.xaml
+++ b/Flow.Launcher/Languages/pt-br.xaml
@@ -55,7 +55,9 @@
Atalho
Atalho do Flow Launcher
+ Modificadores de resultado aberto
Atalho de Consulta Personalizada
+ Mostrar tecla de atalho
Apagar
Editar
Adicionar
diff --git a/Flow.Launcher/Languages/ru.xaml b/Flow.Launcher/Languages/ru.xaml
index 66a463dcb..1855a210c 100644
--- a/Flow.Launcher/Languages/ru.xaml
+++ b/Flow.Launcher/Languages/ru.xaml
@@ -51,7 +51,9 @@
Горячие клавиши
Горячая клавиша Flow Launcher
+ Модификаторы открытого результата
Задаваемые горячие клавиши для запросов
+ Показать Hotkey
Удалить
Изменить
Добавить
diff --git a/Flow.Launcher/Languages/sk.xaml b/Flow.Launcher/Languages/sk.xaml
index 824096c4c..deb79cde4 100644
--- a/Flow.Launcher/Languages/sk.xaml
+++ b/Flow.Launcher/Languages/sk.xaml
@@ -56,7 +56,9 @@
Klávesová skratka
Klávesová skratka pre Flow Launcher
+ Otvorte modifikátory výsledkov
Vlastná klávesová skratka pre dopyt
+ Zobraziť klávesovú skratku
Odstrániť
Upraviť
Pridať
diff --git a/Flow.Launcher/Languages/sr.xaml b/Flow.Launcher/Languages/sr.xaml
index 22aabb37f..1afff3fcf 100644
--- a/Flow.Launcher/Languages/sr.xaml
+++ b/Flow.Launcher/Languages/sr.xaml
@@ -55,6 +55,8 @@
Prečica
Flow Launcher prečica
+ Отворите модификаторе резултата
+ покажи хоткеи
prečica za ručno dodat upit
Obriši
Izmeni
diff --git a/Flow.Launcher/Languages/tr.xaml b/Flow.Launcher/Languages/tr.xaml
index cc287f372..04256d6b0 100644
--- a/Flow.Launcher/Languages/tr.xaml
+++ b/Flow.Launcher/Languages/tr.xaml
@@ -59,7 +59,9 @@
Kısayol Tuşu
Flow Launcher Kısayolu
+ Açık Sonuç Değiştiricileri
Özel Sorgu Kısayolları
+ Kısayol Tuşunu Göster
Sil
Düzenle
Ekle
diff --git a/Flow.Launcher/Languages/uk-UA.xaml b/Flow.Launcher/Languages/uk-UA.xaml
index c7e84c231..af65e0e63 100644
--- a/Flow.Launcher/Languages/uk-UA.xaml
+++ b/Flow.Launcher/Languages/uk-UA.xaml
@@ -51,7 +51,9 @@
Гарячі клавіші
Гаряча клавіша Flow Launcher
+ Відкриті модифікатори результатів
Задані гарячі клавіші для запитів
+ Показати клавішу швидкого доступу
Видалити
Змінити
Додати
diff --git a/Flow.Launcher/Languages/zh-cn.xaml b/Flow.Launcher/Languages/zh-cn.xaml
index 5801b678a..7367de366 100644
--- a/Flow.Launcher/Languages/zh-cn.xaml
+++ b/Flow.Launcher/Languages/zh-cn.xaml
@@ -58,6 +58,8 @@
热键
Flow Launcher激活热键
+ 开放结果修饰符
+ 显示热键
自定义查询热键
删除
编辑
diff --git a/Flow.Launcher/Languages/zh-tw.xaml b/Flow.Launcher/Languages/zh-tw.xaml
index f1df1a6d5..8cd3bbe8c 100644
--- a/Flow.Launcher/Languages/zh-tw.xaml
+++ b/Flow.Launcher/Languages/zh-tw.xaml
@@ -51,7 +51,9 @@
熱鍵
Flow Launcher 執行熱鍵
+ 開放結果修飾符
自定義熱鍵查詢
+ 顯示熱鍵
刪除
編輯
新增
diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml
index 74185fcbd..485f58b47 100644
--- a/Flow.Launcher/MainWindow.xaml
+++ b/Flow.Launcher/MainWindow.xaml
@@ -48,15 +48,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/Resources/app.ico b/Flow.Launcher/Resources/app.ico
index 38c401c8c..362c8747f 100644
Binary files a/Flow.Launcher/Resources/app.ico and b/Flow.Launcher/Resources/app.ico differ
diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml
index c9ca658f2..d21c014d6 100644
--- a/Flow.Launcher/ResultListBox.xaml
+++ b/Flow.Launcher/ResultListBox.xaml
@@ -33,6 +33,8 @@
Cursor="Hand" UseLayoutRounding="False">
+
+
@@ -46,6 +48,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml
index be87e4f52..c8463f59a 100644
--- a/Flow.Launcher/SettingWindow.xaml
+++ b/Flow.Launcher/SettingWindow.xaml
@@ -287,9 +287,10 @@
+
-
+
-
+
+
+
+
+
+
+
+ Margin="0 5 0 0" Grid.Row="3">
@@ -322,7 +333,7 @@
-
diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs
index c677d7c71..c34bbade1 100644
--- a/Flow.Launcher/ViewModel/MainViewModel.cs
+++ b/Flow.Launcher/ViewModel/MainViewModel.cs
@@ -26,6 +26,8 @@ namespace Flow.Launcher.ViewModel
{
#region Private Fields
+ private const string DefaultOpenResultModifiers = "Alt";
+
private bool _isQueryRunning;
private Query _lastQuery;
private string _queryTextBeforeLeaveResults;
@@ -74,6 +76,7 @@ namespace Flow.Launcher.ViewModel
SetHotkey(_settings.Hotkey, OnHotkey);
SetCustomPluginHotkey();
+ SetOpenResultModifiers();
}
private void RegisterResultsUpdatedEvent()
@@ -277,6 +280,8 @@ namespace Flow.Launcher.ViewModel
public ICommand LoadHistoryCommand { get; set; }
public ICommand OpenResultCommand { get; set; }
+ public string OpenResultCommandModifiers { get; private set; }
+
#endregion
public void Query()
@@ -594,6 +599,11 @@ namespace Flow.Launcher.ViewModel
}
}
+ private void SetOpenResultModifiers()
+ {
+ OpenResultCommandModifiers = _settings.OpenResultModifiers ?? DefaultOpenResultModifiers;
+ }
+
private void OnHotkey(object sender, HotkeyEventArgs e)
{
if (!ShouldIgnoreHotkeys())
diff --git a/Flow.Launcher/ViewModel/ResultViewModel.cs b/Flow.Launcher/ViewModel/ResultViewModel.cs
index 69040ee25..0a9731f18 100644
--- a/Flow.Launcher/ViewModel/ResultViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultViewModel.cs
@@ -1,9 +1,11 @@
using System;
+using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Image;
using Flow.Launcher.Infrastructure.Logger;
+using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
@@ -11,14 +13,22 @@ namespace Flow.Launcher.ViewModel
{
public class ResultViewModel : BaseModel
{
- public ResultViewModel(Result result)
+ public ResultViewModel(Result result, Settings settings)
{
if (result != null)
{
Result = result;
}
+
+ Settings = settings;
}
+ public Settings Settings { get; private set; }
+
+ public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
+
+ public string OpenResultModifiers => Settings.OpenResultModifiers;
+
public ImageSource Image
{
get
diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs
index 911cd5807..d30854180 100644
--- a/Flow.Launcher/ViewModel/ResultsViewModel.cs
+++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs
@@ -156,7 +156,7 @@ namespace Flow.Launcher.ViewModel
private List NewResults(List newRawResults, string resultId)
{
var results = Results.ToList();
- var newResults = newRawResults.Select(r => new ResultViewModel(r)).ToList();
+ var newResults = newRawResults.Select(r => new ResultViewModel(r, _settings)).ToList();
var oldResults = results.Where(r => r.Result.PluginID == resultId).ToList();
// Find the same results in A (old results) and B (new newResults)
diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
index 1a5d91a49..3899e7fbd 100644
--- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
+++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs
@@ -139,6 +139,7 @@ namespace Flow.Launcher.ViewModel
}
}
+ public List OpenResultModifiersList => new List { KeyConstant.Alt, KeyConstant.Ctrl, $"{KeyConstant.Ctrl}+{KeyConstant.Alt}" };
private Internationalization _translater => InternationalizationManager.Instance;
public List Languages => _translater.LoadAvailableLanguages();
public IEnumerable MaxResultsRange => Enumerable.Range(2, 16);
diff --git a/Flow.Launcher/app.png b/Flow.Launcher/app.png
index 8c9ca7971..5b75521c8 100644
Binary files a/Flow.Launcher/app.png and b/Flow.Launcher/app.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.Folder/Main.cs b/Plugins/Flow.Launcher.Plugin.Folder/Main.cs
index 74ce0c92d..7b44b110f 100644
--- a/Plugins/Flow.Launcher.Plugin.Folder/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Folder/Main.cs
@@ -1,6 +1,6 @@
using System;
+using System.Collections;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
@@ -27,6 +27,8 @@ namespace Flow.Launcher.Plugin.Folder
private readonly PluginJsonStorage _storage;
private IContextMenu _contextMenuLoader;
+ private static Dictionary _envStringPaths;
+
public Main()
{
_storage = new PluginJsonStorage();
@@ -48,6 +50,7 @@ namespace Flow.Launcher.Plugin.Folder
_context = context;
_contextMenuLoader = new ContextMenuLoader(context);
InitialDriverList();
+ LoadEnvironmentStringPaths();
}
public List Query(Query query)
@@ -55,10 +58,19 @@ namespace Flow.Launcher.Plugin.Folder
var results = GetUserFolderResults(query);
string search = query.Search.ToLower();
- if (!IsDriveOrSharedFolder(search))
+ if (!IsDriveOrSharedFolder(search) && !IsEnvironmentVariableSearch(search))
+ {
return results;
+ }
- results.AddRange(QueryInternal_Directory_Exists(query));
+ if (IsEnvironmentVariableSearch(search))
+ {
+ results.AddRange(GetEnvironmentStringPathResults(search, query));
+ }
+ else
+ {
+ results.AddRange(QueryInternal_Directory_Exists(query.Search, query));
+ }
// todo why was this hack here?
foreach (var result in results)
@@ -69,10 +81,15 @@ namespace Flow.Launcher.Plugin.Folder
return results;
}
+ private static bool IsEnvironmentVariableSearch(string search)
+ {
+ return _envStringPaths != null && search.StartsWith("%");
+ }
+
private static bool IsDriveOrSharedFolder(string search)
{
if (search.StartsWith(@"\\"))
- { // share folder
+ { // shared folder
return true;
}
@@ -146,14 +163,26 @@ namespace Flow.Launcher.Plugin.Folder
}
}
+ private void LoadEnvironmentStringPaths()
+ {
+ _envStringPaths = new Dictionary();
+
+ foreach (DictionaryEntry special in Environment.GetEnvironmentVariables())
+ {
+ if (Directory.Exists(special.Value.ToString()))
+ {
+ _envStringPaths.Add(special.Key.ToString().ToLower(), special.Value.ToString());
+ }
+ }
+ }
+
private static readonly char[] _specialSearchChars = new char[]
{
'?', '*', '>'
};
- private List QueryInternal_Directory_Exists(Query query)
+ private List QueryInternal_Directory_Exists(string search, Query query)
{
- var search = query.Search;
var results = new List();
var hasSpecial = search.IndexOfAny(_specialSearchChars) >= 0;
string incompleteName = "";
@@ -243,6 +272,63 @@ namespace Flow.Launcher.Plugin.Folder
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
}
+ private List GetEnvironmentStringPathSuggestions(string search, Query query)
+ {
+ var results = new List();
+ foreach (var p in _envStringPaths)
+ {
+ if (p.Key.StartsWith(search))
+ {
+ results.Add(CreateFolderResult($"%{p.Key}%", p.Value, p.Value, query));
+ }
+ }
+ return results;
+ }
+
+ private List GetEnvironmentStringPathResults(string envStringSearch, Query query)
+ {
+ if (envStringSearch == "%")
+ { // return all environment string options as path suggestions
+ return GetEnvironmentStringPathSuggestions("", query);
+ }
+
+ var results = new List();
+ var search = envStringSearch.Substring(1);
+
+ if (search.EndsWith("%") && search.Length > 1)
+ { // query starts and ends with a %, find an exact match from env-string paths
+ var exactEnvStringPath = search.Substring(0, search.Length-1);
+
+ if (_envStringPaths.ContainsKey(exactEnvStringPath))
+ {
+ var expandedPath = _envStringPaths[exactEnvStringPath];
+ results.Add(CreateFolderResult($"%{exactEnvStringPath}%", expandedPath, expandedPath, query));
+ }
+ }
+ else if (search.Contains("%"))
+ { // query starts with a % and contains another % somewhere before the end
+ var splitSearch = search.Split("%");
+ var exactEnvStringPath = splitSearch[0];
+
+ // if there are more than 2 % characters in the query, don't bother
+ if (splitSearch.Length == 2 && _envStringPaths.ContainsKey(exactEnvStringPath))
+ {
+ var queryPartToReplace = $"%{exactEnvStringPath}%";
+ var expandedPath = _envStringPaths[exactEnvStringPath];
+ // replace the %envstring% part of the query with its expanded equivalent
+ var updatedSearch = envStringSearch.Replace(queryPartToReplace, expandedPath);
+
+ results.AddRange(QueryInternal_Directory_Exists(updatedSearch, query));
+ }
+ }
+ else
+ { // query simply starts wtih a %, suggest env-string paths that match the rest of the search
+ results.AddRange(GetEnvironmentStringPathSuggestions(search, query));
+ }
+
+ return results;
+ }
+
private static Result CreateFileResult(string filePath, Query query)
{
var result = new Result
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
index 4bc5ba8cd..6208881d4 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
@@ -110,7 +110,7 @@
-
+
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Images/app.png b/Plugins/Flow.Launcher.Plugin.Sys/Images/app.png
index 8c9ca7971..ba17c1a66 100644
Binary files a/Plugins/Flow.Launcher.Plugin.Sys/Images/app.png and b/Plugins/Flow.Launcher.Plugin.Sys/Images/app.png differ
diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
index 21695ffcf..d159e9bc0 100644
--- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
+++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj
@@ -8,6 +8,7 @@
Flow.Launcher.Plugin.WebSearch
false
false
+ en
diff --git a/Plugins/HelloWorldCSharp/Images/app.png b/Plugins/HelloWorldCSharp/Images/app.png
index 8c9ca7971..ba17c1a66 100644
Binary files a/Plugins/HelloWorldCSharp/Images/app.png and b/Plugins/HelloWorldCSharp/Images/app.png differ
diff --git a/Plugins/HelloWorldFSharp/HelloWorldFSharp.fsproj b/Plugins/HelloWorldFSharp/HelloWorldFSharp.fsproj
new file mode 100644
index 000000000..d688ab6a9
--- /dev/null
+++ b/Plugins/HelloWorldFSharp/HelloWorldFSharp.fsproj
@@ -0,0 +1,36 @@
+
+
+
+ netcoreapp3.1
+ false
+ false
+
+
+
+ ..\..\Output\Debug\Plugins\HelloWorldFSharp\
+ DEBUG;TRACE
+
+
+
+ ..\..\Output\Release\Plugins\HelloWorldFSharp\
+
+
+
+
+ PreserveNewest
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Plugins/HelloWorldFSharp/Images/app.png b/Plugins/HelloWorldFSharp/Images/app.png
new file mode 100644
index 000000000..ba17c1a66
Binary files /dev/null and b/Plugins/HelloWorldFSharp/Images/app.png differ
diff --git a/Plugins/HelloWorldFSharp/Main.fs b/Plugins/HelloWorldFSharp/Main.fs
new file mode 100644
index 000000000..15fd3afbd
--- /dev/null
+++ b/Plugins/HelloWorldFSharp/Main.fs
@@ -0,0 +1,31 @@
+namespace HelloWorldFSharp
+
+open Flow.Launcher.Plugin
+open System.Collections.Generic
+
+type HelloWorldFSharpPlugin() =
+
+ let mutable initContext = PluginInitContext()
+
+ interface IPlugin with
+ member this.Init (context: PluginInitContext) =
+ initContext <- context
+
+ member this.Query (query: Query) =
+ List [
+ Result (Title = "Hello World from F#",
+ SubTitle = sprintf "Query: %s" query.Search)
+
+ Result (Title = "Browse source code of this plugin",
+ SubTitle = "click to open in browser",
+ Action = (fun ctx ->
+ initContext.CurrentPluginMetadata.Website
+ |> System.Diagnostics.Process.Start
+ |> ignore
+ true))
+
+ Result (Title = "Trigger a tray message",
+ Action = (fun _ ->
+ initContext.API.ShowMsg ("Sample tray message", "from the F# plugin")
+ false))
+ ]
diff --git a/Plugins/HelloWorldFSharp/plugin.json b/Plugins/HelloWorldFSharp/plugin.json
new file mode 100644
index 000000000..845b93e4b
--- /dev/null
+++ b/Plugins/HelloWorldFSharp/plugin.json
@@ -0,0 +1,12 @@
+{
+ "ID": "8FF5D5C1F8194958A12E8668FB7ECC04",
+ "ActionKeyword": "hf",
+ "Name": "Hello World FSharp",
+ "Description": "Hello World FSharp",
+ "Author": "Ioannis G.",
+ "Version": "1.0.0",
+ "Language": "fsharp",
+ "Website": "https://github.com/Flow-Launcher/Flow.Launcher",
+ "ExecuteFileName": "HelloWorldFSharp.dll",
+ "IcoPath": "Images\\app.png"
+}
\ No newline at end of file
diff --git a/Plugins/HelloWorldPython/Images/app.png b/Plugins/HelloWorldPython/Images/app.png
index 8c9ca7971..ba17c1a66 100644
Binary files a/Plugins/HelloWorldPython/Images/app.png and b/Plugins/HelloWorldPython/Images/app.png differ