Skip to content

Commit 80fc62e

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents eeadcdf + db358e6 commit 80fc62e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+481
-108
lines changed

Doc/app.ico

-31.1 KB
Binary file not shown.

Doc/app.png

-5.91 KB
Loading

Doc/app.psd

-77.5 KB
Binary file not shown.

Doc/app_error.png

-9.06 KB
Loading

Doc/app_error.psd

-77.5 KB
Binary file not shown.

Flow.Launcher.Core/Flow.Launcher.Core.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
1212
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
1313
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
14+
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
1415
</PropertyGroup>
1516

1617
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -52,10 +53,11 @@
5253
</ItemGroup>
5354

5455
<ItemGroup>
56+
<PackageReference Include="FSharp.Core" Version="4.7.1" />
5557
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
5658
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
5759
<PackageReference Include="squirrel.windows" Version="1.5.2" />
58-
<PackageReference Include="PropertyChanged.Fody" Version="2.2.4">
60+
<PackageReference Include="PropertyChanged.Fody" Version="3.2.8">
5961
<PrivateAssets>all</PrivateAssets>
6062
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6163
</PackageReference>

Flow.Launcher.Core/Plugin/PluginsLoader.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,21 @@ public static class PluginsLoader
2020

2121
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
2222
{
23-
var csharpPlugins = CSharpPlugins(metadatas).ToList();
23+
var dotnetPlugins = DotNetPlugins(metadatas).ToList();
2424
var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory);
2525
var executablePlugins = ExecutablePlugins(metadatas);
26-
var plugins = csharpPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
26+
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
2727
return plugins;
2828
}
2929

30-
public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
30+
public static IEnumerable<PluginPair> DotNetPlugins(List<PluginMetadata> source)
3131
{
3232
var plugins = new List<PluginPair>();
33-
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp);
33+
var metadatas = source.Where(o => AllowedLanguage.IsDotNet(o.Language));
3434

3535
foreach (var metadata in metadatas)
3636
{
37-
var milliseconds = Stopwatch.Debug($"|PluginsLoader.CSharpPlugins|Constructor init cost for {metadata.Name}", () =>
37+
var milliseconds = Stopwatch.Debug($"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
3838
{
3939

4040
#if DEBUG
@@ -50,7 +50,7 @@ public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
5050
}
5151
catch (Exception e)
5252
{
53-
Log.Exception($"|PluginsLoader.CSharpPlugins|Couldn't load assembly for {metadata.Name}", e);
53+
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for {metadata.Name}", e);
5454
return;
5555
}
5656
var types = assembly.GetTypes();
@@ -61,7 +61,7 @@ public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
6161
}
6262
catch (InvalidOperationException e)
6363
{
64-
Log.Exception($"|PluginsLoader.CSharpPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e);
64+
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find class implement IPlugin for <{metadata.Name}>", e);
6565
return;
6666
}
6767
IPlugin plugin;
@@ -71,7 +71,7 @@ public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
7171
}
7272
catch (Exception e)
7373
{
74-
Log.Exception($"|PluginsLoader.CSharpPlugins|Can't create instance for <{metadata.Name}>", e);
74+
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't create instance for <{metadata.Name}>", e);
7575
return;
7676
}
7777
#endif
-9.06 KB
Loading

Flow.Launcher.Infrastructure/Image/ImageLoader.cs

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ namespace Flow.Launcher.Infrastructure.Image
1313
{
1414
public static class ImageLoader
1515
{
16-
private static readonly ImageCache ImageCache = new ImageCache();
16+
private static readonly ImageCache _imageCache = new ImageCache();
17+
private static readonly ConcurrentDictionary<string, string> _guidToKey = new ConcurrentDictionary<string, string>();
18+
private static readonly bool _enableHashImage = true;
19+
1720
private static BinaryStorage<Dictionary<string, int>> _storage;
18-
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
1921
private static IImageHashGenerator _hashGenerator;
2022

21-
2223
private static readonly string[] ImageExtensions =
2324
{
2425
".png",
@@ -30,38 +31,38 @@ public static class ImageLoader
3031
".ico"
3132
};
3233

33-
3434
public static void Initialize()
3535
{
3636
_storage = new BinaryStorage<Dictionary<string, int>>("Image");
3737
_hashGenerator = new ImageHashGenerator();
3838

39-
ImageCache.Usage = LoadStorageToConcurrentDictionary();
39+
_imageCache.Usage = LoadStorageToConcurrentDictionary();
4040

4141
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
4242
{
4343
ImageSource img = new BitmapImage(new Uri(icon));
4444
img.Freeze();
45-
ImageCache[icon] = img;
45+
_imageCache[icon] = img;
4646
}
47+
4748
Task.Run(() =>
4849
{
4950
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
5051
{
51-
ImageCache.Usage.AsParallel().ForAll(x =>
52+
_imageCache.Usage.AsParallel().ForAll(x =>
5253
{
5354
Load(x.Key);
5455
});
5556
});
56-
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
57+
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{_imageCache.Usage.Count}>, Images Number: {_imageCache.CacheSize()}, Unique Items {_imageCache.UniqueImagesInCache()}");
5758
});
5859
}
5960

6061
public static void Save()
6162
{
6263
lock (_storage)
6364
{
64-
_storage.Save(ImageCache.CleanupAndToDictionary());
65+
_storage.Save(_imageCache.CleanupAndToDictionary());
6566
}
6667
}
6768

@@ -99,17 +100,17 @@ private enum ImageType
99100

100101
private static ImageResult LoadInternal(string path, bool loadFullImage = false)
101102
{
102-
ImageSource image;
103-
ImageType type = ImageType.Error;
103+
ImageResult imageResult;
104+
104105
try
105106
{
106107
if (string.IsNullOrEmpty(path))
107108
{
108-
return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
109+
return new ImageResult(_imageCache[Constant.ErrorIcon], ImageType.Error);
109110
}
110-
if (ImageCache.ContainsKey(path))
111+
if (_imageCache.ContainsKey(path))
111112
{
112-
return new ImageResult(ImageCache[path], ImageType.Cache);
113+
return new ImageResult(_imageCache[path], ImageType.Cache);
113114
}
114115

115116
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
@@ -124,95 +125,121 @@ private static ImageResult LoadInternal(string path, bool loadFullImage = false)
124125
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
125126
}
126127

127-
if (Directory.Exists(path))
128+
imageResult = GetThumbnailResult(ref path, loadFullImage);
129+
}
130+
catch (System.Exception e)
131+
{
132+
try
133+
{
134+
// Get thumbnail may fail for certain images on the first try, retry again has proven to work
135+
imageResult = GetThumbnailResult(ref path, loadFullImage);
136+
}
137+
catch (System.Exception e2)
128138
{
129-
/* Directories can also have thumbnails instead of shell icons.
130-
* Generating thumbnails for a bunch of folders while scrolling through
131-
* results from Everything makes a big impact on performance and
132-
* Flow.Launcher responsibility.
133-
* - Solution: just load the icon
134-
*/
135-
type = ImageType.Folder;
136-
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
137-
Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
139+
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
140+
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
138141

142+
ImageSource image = _imageCache[Constant.ErrorIcon];
143+
_imageCache[path] = image;
144+
imageResult = new ImageResult(image, ImageType.Error);
139145
}
140-
else if (File.Exists(path))
146+
}
147+
148+
return imageResult;
149+
}
150+
151+
private static ImageResult GetThumbnailResult(ref string path, bool loadFullImage = false)
152+
{
153+
ImageSource image;
154+
ImageType type = ImageType.Error;
155+
156+
if (Directory.Exists(path))
157+
{
158+
/* Directories can also have thumbnails instead of shell icons.
159+
* Generating thumbnails for a bunch of folders while scrolling through
160+
* results from Everything makes a big impact on performance and
161+
* Flow.Launcher responsibility.
162+
* - Solution: just load the icon
163+
*/
164+
type = ImageType.Folder;
165+
image = GetThumbnail(path, ThumbnailOptions.IconOnly);
166+
}
167+
else if (File.Exists(path))
168+
{
169+
var extension = Path.GetExtension(path).ToLower();
170+
if (ImageExtensions.Contains(extension))
141171
{
142-
var extension = Path.GetExtension(path).ToLower();
143-
if (ImageExtensions.Contains(extension))
172+
type = ImageType.ImageFile;
173+
if (loadFullImage)
144174
{
145-
type = ImageType.ImageFile;
146-
if (loadFullImage)
147-
{
148-
image = LoadFullImage(path);
149-
}
150-
else
151-
{
152-
/* Although the documentation for GetImage on MSDN indicates that
153-
* if a thumbnail is available it will return one, this has proved to not
154-
* be the case in many situations while testing.
155-
* - Solution: explicitly pass the ThumbnailOnly flag
156-
*/
157-
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
158-
Constant.ThumbnailSize, ThumbnailOptions.ThumbnailOnly);
159-
}
175+
image = LoadFullImage(path);
160176
}
161177
else
162178
{
163-
type = ImageType.File;
164-
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
165-
Constant.ThumbnailSize, ThumbnailOptions.None);
179+
/* Although the documentation for GetImage on MSDN indicates that
180+
* if a thumbnail is available it will return one, this has proved to not
181+
* be the case in many situations while testing.
182+
* - Solution: explicitly pass the ThumbnailOnly flag
183+
*/
184+
image = GetThumbnail(path, ThumbnailOptions.ThumbnailOnly);
166185
}
167186
}
168187
else
169188
{
170-
image = ImageCache[Constant.ErrorIcon];
171-
path = Constant.ErrorIcon;
172-
}
173-
174-
if (type != ImageType.Error)
175-
{
176-
image.Freeze();
189+
type = ImageType.File;
190+
image = GetThumbnail(path, ThumbnailOptions.None);
177191
}
178192
}
179-
catch (System.Exception e)
193+
else
194+
{
195+
image = _imageCache[Constant.ErrorIcon];
196+
path = Constant.ErrorIcon;
197+
}
198+
199+
if (type != ImageType.Error)
180200
{
181-
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
182-
type = ImageType.Error;
183-
image = ImageCache[Constant.ErrorIcon];
184-
ImageCache[path] = image;
201+
image.Freeze();
185202
}
203+
186204
return new ImageResult(image, type);
187205
}
188206

189-
private static bool EnableImageHash = true;
207+
private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly)
208+
{
209+
return WindowsThumbnailProvider.GetThumbnail(
210+
path,
211+
Constant.ThumbnailSize,
212+
Constant.ThumbnailSize,
213+
option);
214+
}
190215

191216
public static ImageSource Load(string path, bool loadFullImage = false)
192217
{
193218
var imageResult = LoadInternal(path, loadFullImage);
194219

195220
var img = imageResult.ImageSource;
196221
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
197-
{ // we need to get image hash
198-
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
222+
{
223+
// we need to get image hash
224+
string hash = _enableHashImage ? _hashGenerator.GetHashFromImage(img) : null;
199225
if (hash != null)
200226
{
201-
if (GuidToKey.TryGetValue(hash, out string key))
202-
{ // image already exists
203-
img = ImageCache[key];
227+
if (_guidToKey.TryGetValue(hash, out string key))
228+
{
229+
// image already exists
230+
img = _imageCache[key];
204231
}
205232
else
206-
{ // new guid
207-
GuidToKey[hash] = path;
233+
{
234+
// new guid
235+
_guidToKey[hash] = path;
208236
}
209237
}
210238

211239
// update cache
212-
ImageCache[path] = img;
240+
_imageCache[path] = img;
213241
}
214242

215-
216243
return img;
217244
}
218245

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Flow.Launcher.Infrastructure
2+
{
3+
public static class KeyConstant
4+
{
5+
public const string Ctrl = nameof(Ctrl);
6+
public const string Alt = nameof(Alt);
7+
public const string Space = nameof(Space);
8+
}
9+
}

0 commit comments

Comments
 (0)