mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #221 from taooceros/fixImageFlickering
Fix image flickering
This commit is contained in:
commit
2608d961d0
4 changed files with 83 additions and 30 deletions
|
|
@ -26,7 +26,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
private const int MaxCached = 50;
|
||||
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
|
||||
private const int permissibleFactor = 2;
|
||||
|
||||
|
||||
public void Initialization(Dictionary<string, int> usage)
|
||||
{
|
||||
foreach (var key in usage.Keys)
|
||||
|
|
@ -44,14 +44,14 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
value.usage++;
|
||||
return value.imageSource;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
Data.AddOrUpdate(
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
(k, v) =>
|
||||
{
|
||||
v.imageSource = value;
|
||||
|
|
@ -66,7 +66,6 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
// To delete the images from the data dictionary based on the resizing of the Usage Dictionary.
|
||||
|
||||
|
||||
foreach (var key in Data.OrderBy(x => x.Value.usage).Take(Data.Count - MaxCached).Select(x => x.Key))
|
||||
{
|
||||
if (!(key.Equals(Constant.ErrorIcon) || key.Equals(Constant.DefaultIcon)))
|
||||
|
|
@ -80,7 +79,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
var contains = Data.ContainsKey(key);
|
||||
var contains = Data.ContainsKey(key) && Data[key] != null;
|
||||
return contains;
|
||||
}
|
||||
|
||||
|
|
@ -97,5 +96,4 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return Data.Values.Select(x => x.imageSource).Distinct().Count();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
|
||||
private static IImageHashGenerator _hashGenerator;
|
||||
private static bool EnableImageHash = true;
|
||||
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
|
||||
|
||||
private static readonly string[] ImageExtensions =
|
||||
{
|
||||
|
|
@ -61,7 +63,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, y => y.usage));
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +213,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
option);
|
||||
}
|
||||
|
||||
public static bool CacheContainImage(string path)
|
||||
{
|
||||
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
|
||||
}
|
||||
|
||||
public static ImageSource Load(string path, bool loadFullImage = false)
|
||||
{
|
||||
var imageResult = LoadInternal(path, loadFullImage);
|
||||
|
|
@ -221,7 +228,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
|
||||
if (hash != null)
|
||||
{
|
||||
|
||||
|
||||
if (GuidToKey.TryGetValue(hash, out string key))
|
||||
{ // image already exists
|
||||
img = ImageCache[key] ?? img;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
<ColumnDefinition Width="0" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="ImageIcon" Width="32" Height="32" HorizontalAlignment="Left"
|
||||
Source="{Binding Image ,IsAsync=True}" />
|
||||
Source="{Binding Image.Value}" />
|
||||
<Grid Margin="5 0 5 0" Grid.Column="1" HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
|
|
|
|||
|
|
@ -1,23 +1,66 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
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;
|
||||
|
||||
|
||||
namespace Flow.Launcher.ViewModel
|
||||
{
|
||||
public class ResultViewModel : BaseModel
|
||||
{
|
||||
public class LazyAsync<T> : Lazy<Task<T>>
|
||||
{
|
||||
private T defaultValue;
|
||||
|
||||
private readonly Action _updateCallback;
|
||||
public new T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValueCreated)
|
||||
{
|
||||
base.Value.ContinueWith(_ =>
|
||||
{
|
||||
_updateCallback();
|
||||
});
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (!base.Value.IsCompleted || base.Value.IsFaulted)
|
||||
return defaultValue;
|
||||
|
||||
return base.Value.Result;
|
||||
}
|
||||
}
|
||||
public LazyAsync(Func<Task<T>> factory, T defaultValue, Action updateCallback) : base(factory)
|
||||
{
|
||||
if (defaultValue != null)
|
||||
{
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
_updateCallback = updateCallback;
|
||||
}
|
||||
}
|
||||
|
||||
public ResultViewModel(Result result, Settings settings)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Result = result;
|
||||
|
||||
Image = new LazyAsync<ImageSource>(
|
||||
SetImage,
|
||||
ImageLoader.DefaultImage,
|
||||
() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(Image));
|
||||
});
|
||||
}
|
||||
|
||||
Settings = settings;
|
||||
|
|
@ -25,39 +68,45 @@ namespace Flow.Launcher.ViewModel
|
|||
|
||||
public Settings Settings { get; private set; }
|
||||
|
||||
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
|
||||
public Visibility ShowOpenResultHotkey => Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
public string OpenResultModifiers => Settings.OpenResultModifiers;
|
||||
|
||||
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
|
||||
? Result.Title
|
||||
? Result.Title
|
||||
: Result.TitleToolTip;
|
||||
|
||||
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
|
||||
? Result.SubTitle
|
||||
? Result.SubTitle
|
||||
: Result.SubTitleToolTip;
|
||||
|
||||
public ImageSource Image
|
||||
public LazyAsync<ImageSource> Image { get; set; }
|
||||
|
||||
private async Task<ImageSource> SetImage()
|
||||
{
|
||||
get
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
{
|
||||
var imagePath = Result.IcoPath;
|
||||
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return Result.Icon();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
imagePath = Constant.MissingImgIcon;
|
||||
}
|
||||
return Result.Icon();
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>", e);
|
||||
imagePath = Constant.MissingImgIcon;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImageLoader.CacheContainImage(imagePath))
|
||||
{
|
||||
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
|
||||
return ImageLoader.Load(imagePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await Task.Run(() => ImageLoader.Load(imagePath));
|
||||
}
|
||||
}
|
||||
|
||||
public Result Result { get; }
|
||||
|
|
@ -84,6 +133,5 @@ namespace Flow.Launcher.ViewModel
|
|||
{
|
||||
return Result.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue