2016-08-18 00:16:40 +00:00
|
|
|
|
using System;
|
2020-11-15 12:45:00 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2020-05-03 21:36:23 +00:00
|
|
|
|
using System.Windows;
|
2016-08-18 00:16:40 +00:00
|
|
|
|
using System.Windows.Media;
|
2020-04-21 09:12:17 +00:00
|
|
|
|
using Flow.Launcher.Infrastructure.Image;
|
|
|
|
|
|
using Flow.Launcher.Infrastructure.Logger;
|
2020-05-03 21:36:23 +00:00
|
|
|
|
using Flow.Launcher.Infrastructure.UserSettings;
|
2020-04-21 09:12:17 +00:00
|
|
|
|
using Flow.Launcher.Plugin;
|
2021-08-06 09:04:19 +00:00
|
|
|
|
using System.IO;
|
2021-12-27 02:22:52 +00:00
|
|
|
|
using System.Drawing.Text;
|
|
|
|
|
|
using System.Collections.Generic;
|
2022-09-16 06:04:18 +00:00
|
|
|
|
using System.Drawing;
|
2016-02-18 11:31:15 +00:00
|
|
|
|
|
2020-04-21 09:12:17 +00:00
|
|
|
|
namespace Flow.Launcher.ViewModel
|
2016-02-18 11:31:15 +00:00
|
|
|
|
{
|
2016-05-23 21:08:13 +00:00
|
|
|
|
public class ResultViewModel : BaseModel
|
2016-02-18 11:31:15 +00:00
|
|
|
|
{
|
2021-12-27 02:22:52 +00:00
|
|
|
|
private static PrivateFontCollection fontCollection = new();
|
2022-09-13 22:34:11 +00:00
|
|
|
|
private static Dictionary<string, string> fonts = new();
|
2021-12-27 02:22:52 +00:00
|
|
|
|
|
2020-05-03 21:36:23 +00:00
|
|
|
|
public ResultViewModel(Result result, Settings settings)
|
2016-02-18 11:31:15 +00:00
|
|
|
|
{
|
2016-02-22 21:47:10 +00:00
|
|
|
|
if (result != null)
|
2016-02-18 11:31:15 +00:00
|
|
|
|
{
|
2016-06-22 23:22:41 +00:00
|
|
|
|
Result = result;
|
2020-11-26 20:04:39 +00:00
|
|
|
|
|
2021-08-06 09:15:10 +00:00
|
|
|
|
if (Result.Glyph is { FontFamily: not null } glyph)
|
2021-08-06 09:04:19 +00:00
|
|
|
|
{
|
2021-08-10 10:24:27 +00:00
|
|
|
|
// Checks if it's a system installed font, which does not require path to be provided.
|
2021-08-09 10:38:44 +00:00
|
|
|
|
if (glyph.FontFamily.EndsWith(".ttf") || glyph.FontFamily.EndsWith(".otf"))
|
2021-08-06 09:04:19 +00:00
|
|
|
|
{
|
2021-12-27 02:22:52 +00:00
|
|
|
|
string fontFamilyPath = glyph.FontFamily;
|
|
|
|
|
|
|
|
|
|
|
|
if (!Path.IsPathRooted(fontFamilyPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
fontFamilyPath = Path.Combine(Result.PluginDirectory, fontFamilyPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (fonts.ContainsKey(fontFamilyPath))
|
|
|
|
|
|
{
|
|
|
|
|
|
Glyph = glyph with
|
2021-08-09 10:38:44 +00:00
|
|
|
|
{
|
2021-12-27 02:22:52 +00:00
|
|
|
|
FontFamily = fonts[fontFamilyPath]
|
2021-08-09 10:38:44 +00:00
|
|
|
|
};
|
2021-12-27 02:22:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
fontCollection.AddFontFile(fontFamilyPath);
|
|
|
|
|
|
fonts[fontFamilyPath] = $"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}";
|
|
|
|
|
|
Glyph = glyph with
|
|
|
|
|
|
{
|
|
|
|
|
|
FontFamily = fonts[fontFamilyPath]
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2021-08-06 09:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Glyph = glyph;
|
|
|
|
|
|
}
|
2021-08-06 09:04:19 +00:00
|
|
|
|
}
|
2021-07-31 07:44:41 +00:00
|
|
|
|
}
|
2020-05-03 21:36:23 +00:00
|
|
|
|
|
|
|
|
|
|
Settings = settings;
|
2016-02-18 11:31:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-09 10:38:44 +00:00
|
|
|
|
private Settings Settings { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public Visibility ShowOpenResultHotkey =>
|
2021-09-27 11:00:46 +00:00
|
|
|
|
Settings.ShowOpenResultHotkey ? Visibility.Visible : Visibility.Collapsed;
|
2020-05-03 21:36:23 +00:00
|
|
|
|
|
2022-09-13 22:34:11 +00:00
|
|
|
|
public Visibility ShowDefaultPreview => Result.PreviewPanel == null ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
|
|
|
|
|
|
|
public Visibility ShowCustomizedPrewview => Result.PreviewPanel == null ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
|
|
|
2021-10-07 10:58:54 +00:00
|
|
|
|
public Visibility ShowIcon
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
// If both glyph and image icons are not available, it will then be the default icon
|
|
|
|
|
|
if (!ImgIconAvailable && !GlyphAvailable)
|
|
|
|
|
|
return Visibility.Visible;
|
|
|
|
|
|
|
|
|
|
|
|
// Although user can choose to use glyph icons, plugins may choose to supply only image icons.
|
|
|
|
|
|
// In this case we ignore the setting because otherwise icons will not display as intended
|
|
|
|
|
|
if (Settings.UseGlyphIcons && !GlyphAvailable && ImgIconAvailable)
|
|
|
|
|
|
return Visibility.Visible;
|
|
|
|
|
|
|
|
|
|
|
|
return !Settings.UseGlyphIcons && ImgIconAvailable ? Visibility.Visible : Visibility.Hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-30 08:27:37 +00:00
|
|
|
|
public double IconRadius
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Result.RoundedIcon)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IconXY / 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
return IconXY;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-07 10:58:54 +00:00
|
|
|
|
public Visibility ShowGlyph
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
// Although user can choose to not use glyph icons, plugins may choose to supply only glyph icons.
|
|
|
|
|
|
// In this case we ignore the setting because otherwise icons will not display as intended
|
|
|
|
|
|
if (!Settings.UseGlyphIcons && !ImgIconAvailable && GlyphAvailable)
|
|
|
|
|
|
return Visibility.Visible;
|
|
|
|
|
|
|
|
|
|
|
|
return Settings.UseGlyphIcons && GlyphAvailable ? Visibility.Visible : Visibility.Hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool GlyphAvailable => Glyph is not null;
|
|
|
|
|
|
|
|
|
|
|
|
private bool ImgIconAvailable => !string.IsNullOrEmpty(Result.IcoPath) || Result.Icon is not null;
|
2020-05-03 21:36:23 +00:00
|
|
|
|
|
|
|
|
|
|
public string OpenResultModifiers => Settings.OpenResultModifiers;
|
|
|
|
|
|
|
2020-06-29 20:45:04 +00:00
|
|
|
|
public string ShowTitleToolTip => string.IsNullOrEmpty(Result.TitleToolTip)
|
2021-08-06 09:04:19 +00:00
|
|
|
|
? Result.Title
|
|
|
|
|
|
: Result.TitleToolTip;
|
2020-06-29 20:45:04 +00:00
|
|
|
|
|
|
|
|
|
|
public string ShowSubTitleToolTip => string.IsNullOrEmpty(Result.SubTitleToolTip)
|
2021-08-06 09:04:19 +00:00
|
|
|
|
? Result.SubTitle
|
|
|
|
|
|
: Result.SubTitleToolTip;
|
2020-11-15 12:45:00 +00:00
|
|
|
|
|
2021-07-31 08:15:49 +00:00
|
|
|
|
private volatile bool ImageLoaded;
|
2022-09-02 11:40:37 +00:00
|
|
|
|
private volatile bool PreviewImageLoaded;
|
2021-05-23 04:28:45 +00:00
|
|
|
|
|
|
|
|
|
|
private ImageSource image = ImageLoader.DefaultImage;
|
2022-09-02 11:40:37 +00:00
|
|
|
|
private ImageSource previewImage = ImageLoader.DefaultImage;
|
2020-06-29 20:45:04 +00:00
|
|
|
|
|
2021-05-23 04:28:45 +00:00
|
|
|
|
public ImageSource Image
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!ImageLoaded)
|
|
|
|
|
|
{
|
|
|
|
|
|
ImageLoaded = true;
|
2021-07-31 08:15:49 +00:00
|
|
|
|
_ = LoadImageAsync();
|
2021-05-23 04:28:45 +00:00
|
|
|
|
}
|
2021-08-09 10:38:44 +00:00
|
|
|
|
|
2021-05-23 04:28:45 +00:00
|
|
|
|
return image;
|
|
|
|
|
|
}
|
|
|
|
|
|
private set => image = value;
|
|
|
|
|
|
}
|
2020-06-29 20:45:04 +00:00
|
|
|
|
|
2022-09-02 11:40:37 +00:00
|
|
|
|
public ImageSource PreviewImage
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!PreviewImageLoaded)
|
|
|
|
|
|
{
|
|
|
|
|
|
PreviewImageLoaded = true;
|
|
|
|
|
|
_ = LoadPreviewImageAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return previewImage;
|
|
|
|
|
|
}
|
|
|
|
|
|
private set => previewImage = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-07-31 07:44:41 +00:00
|
|
|
|
public GlyphInfo Glyph { get; set; }
|
|
|
|
|
|
|
2022-10-30 19:23:04 +00:00
|
|
|
|
private async Task LoadImageAsync()
|
2016-08-18 00:16:40 +00:00
|
|
|
|
{
|
2020-11-16 02:12:32 +00:00
|
|
|
|
var imagePath = Result.IcoPath;
|
|
|
|
|
|
if (string.IsNullOrEmpty(imagePath) && Result.Icon != null)
|
2016-08-18 00:16:40 +00:00
|
|
|
|
{
|
2020-11-16 02:12:32 +00:00
|
|
|
|
try
|
2016-08-18 00:16:40 +00:00
|
|
|
|
{
|
2021-05-24 02:34:16 +00:00
|
|
|
|
image = Result.Icon();
|
2021-05-23 04:28:45 +00:00
|
|
|
|
return;
|
2016-08-18 00:16:40 +00:00
|
|
|
|
}
|
2020-11-16 02:12:32 +00:00
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
2021-08-09 10:38:44 +00:00
|
|
|
|
Log.Exception(
|
|
|
|
|
|
$"|ResultViewModel.Image|IcoPath is empty and exception when calling Icon() for result <{Result.Title}> of plugin <{Result.PluginDirectory}>",
|
|
|
|
|
|
e);
|
2020-11-16 02:12:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-11-15 12:45:00 +00:00
|
|
|
|
|
2022-10-16 09:25:47 +00:00
|
|
|
|
var loadFullImage = (Path.GetExtension(imagePath) ?? "").Equals(".url", StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
|
|
2020-11-16 02:12:32 +00:00
|
|
|
|
if (ImageLoader.CacheContainImage(imagePath))
|
2021-05-23 04:28:45 +00:00
|
|
|
|
{
|
2020-11-16 02:12:32 +00:00
|
|
|
|
// will get here either when icoPath has value\icon delegate is null\when had exception in delegate
|
2022-11-11 22:45:41 +00:00
|
|
|
|
image = await ImageLoader.LoadAsync(imagePath, loadFullImage);
|
2021-05-23 04:28:45 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2021-07-31 07:44:41 +00:00
|
|
|
|
|
2021-05-24 02:35:18 +00:00
|
|
|
|
// We need to modify the property not field here to trigger the OnPropertyChanged event
|
2022-11-24 20:19:16 +00:00
|
|
|
|
Image = await ImageLoader.LoadAsync(imagePath, loadFullImage).ConfigureAwait(false);
|
2016-08-18 00:16:40 +00:00
|
|
|
|
}
|
2016-02-19 15:26:13 +00:00
|
|
|
|
|
2022-09-02 11:40:37 +00:00
|
|
|
|
|
|
|
|
|
|
private async ValueTask LoadPreviewImageAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
var imagePath = Result.PreviewImage ?? Result.IcoPath;
|
2022-11-25 13:03:17 +00:00
|
|
|
|
var loadFullImage = (Path.GetExtension(imagePath) ?? "").Equals(".url", StringComparison.OrdinalIgnoreCase);
|
2022-09-02 11:40:37 +00:00
|
|
|
|
// We need to modify the property not field here to trigger the OnPropertyChanged event
|
2022-11-25 13:03:17 +00:00
|
|
|
|
//PreviewImage = await Task.Run(() => ImageLoader.Load(imagePath, true)).ConfigureAwait(false);
|
|
|
|
|
|
PreviewImage = await ImageLoader.LoadAsync(imagePath, loadFullImage).ConfigureAwait(false);
|
2022-09-02 11:40:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-22 23:22:41 +00:00
|
|
|
|
public Result Result { get; }
|
2022-09-30 00:58:33 +00:00
|
|
|
|
public int ResultProgress
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Result.ProgressBar == null)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
return Result.ProgressBar.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-04-21 00:53:21 +00:00
|
|
|
|
|
2021-12-05 08:15:32 +00:00
|
|
|
|
public string QuerySuggestionText { get; set; }
|
|
|
|
|
|
|
2022-08-30 08:27:17 +00:00
|
|
|
|
public double IconXY { get; set; } = 32;
|
|
|
|
|
|
|
2016-02-18 14:53:41 +00:00
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
|
{
|
2021-05-23 04:28:45 +00:00
|
|
|
|
return obj is ResultViewModel r && Result.Equals(r.Result);
|
2016-02-19 14:55:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
|
{
|
2016-06-22 23:22:41 +00:00
|
|
|
|
return Result.GetHashCode();
|
2016-02-19 14:55:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
|
{
|
2016-06-22 23:22:41 +00:00
|
|
|
|
return Result.ToString();
|
2016-02-18 14:53:41 +00:00
|
|
|
|
}
|
2016-02-18 11:31:15 +00:00
|
|
|
|
}
|
2021-08-10 10:24:27 +00:00
|
|
|
|
}
|