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 ;
2024-01-18 23:31:04 +00:00
using Avalonia.Media.Imaging ;
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
{
2022-11-25 20:44:08 +00:00
Settings = settings ;
if ( result = = null )
2016-02-18 11:31:15 +00:00
{
2022-11-25 20:44:08 +00:00
return ;
}
2024-01-18 22:45:49 +00:00
2022-11-25 20:44:08 +00:00
Result = result ;
2020-11-26 20:04:39 +00:00
2022-11-25 20:44:08 +00:00
if ( Result . Glyph is { FontFamily : not null } glyph )
{
// Checks if it's a system installed font, which does not require path to be provided.
if ( glyph . FontFamily . EndsWith ( ".ttf" ) | | glyph . FontFamily . EndsWith ( ".otf" ) )
2021-08-06 09:04:19 +00:00
{
2022-11-25 20:44:08 +00:00
string fontFamilyPath = glyph . FontFamily ;
2021-12-27 02:22:52 +00:00
2022-11-25 20:44:08 +00:00
if ( ! Path . IsPathRooted ( fontFamilyPath ) )
{
fontFamilyPath = Path . Combine ( Result . PluginDirectory , fontFamilyPath ) ;
}
2021-12-27 02:22:52 +00:00
2022-11-25 20:44:08 +00:00
if ( fonts . ContainsKey ( fontFamilyPath ) )
{
2024-01-18 22:45:49 +00:00
Glyph = glyph with { FontFamily = fonts [ fontFamilyPath ] } ;
2021-08-06 09:15:10 +00:00
}
else
{
2022-11-25 20:44:08 +00:00
fontCollection . AddFontFile ( fontFamilyPath ) ;
2024-01-18 22:45:49 +00:00
fonts [ fontFamilyPath ] =
$"{Path.GetDirectoryName(fontFamilyPath)}/#{fontCollection.Families[^1].Name}" ;
Glyph = glyph with { FontFamily = fonts [ fontFamilyPath ] } ;
2021-08-06 09:15:10 +00:00
}
2021-08-06 09:04:19 +00:00
}
2022-11-25 20:44:08 +00:00
else
{
Glyph = glyph ;
}
2021-07-31 07:44:41 +00:00
}
2024-01-19 03:46:21 +00:00
2024-01-18 23:49:12 +00:00
LoadImage ( ) ;
2016-02-18 11:31:15 +00:00
}
2021-08-09 10:38:44 +00:00
private Settings Settings { get ; }
2024-01-18 22:45:49 +00:00
public bool ShowOpenResultHotkey = > Settings . ShowOpenResultHotkey ;
2020-05-03 21:36:23 +00:00
2024-01-18 23:49:12 +00:00
public Bitmap DefaultImage = > ImageLoader . LoadingImage ;
2024-01-18 22:45:49 +00:00
public bool ShowDefaultPreview = > Result . PreviewPanel ! = null ;
2022-09-13 22:34:11 +00:00
2024-01-18 22:45:49 +00:00
public bool ShowCustomizedPreview = > Result . PreviewPanel ! = null ;
2022-09-13 22:34:11 +00:00
2024-01-18 22:45:49 +00:00
public bool ShowIcon
2021-10-07 10:58:54 +00:00
{
get
{
// If both glyph and image icons are not available, it will then be the default icon
if ( ! ImgIconAvailable & & ! GlyphAvailable )
2024-01-18 22:45:49 +00:00
return true ;
2021-10-07 10:58:54 +00:00
// 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 )
2024-01-18 22:45:49 +00:00
return true ;
2021-10-07 10:58:54 +00:00
2024-01-18 22:45:49 +00:00
return ! Settings . UseGlyphIcons & & ImgIconAvailable ;
2021-10-07 10:58:54 +00:00
}
}
2024-01-18 22:45:49 +00:00
public bool ShowPreviewImage
2022-12-28 13:37:53 +00:00
{
get
{
2022-12-29 05:13:28 +00:00
if ( PreviewImageAvailable )
2022-12-28 13:37:53 +00:00
{
2024-01-18 22:45:49 +00:00
return true ;
2022-12-28 13:37:53 +00:00
}
else
{
2022-12-29 05:13:28 +00:00
// Fall back to icon
return ShowIcon ;
2022-12-28 13:37:53 +00:00
}
}
}
2022-08-30 08:27:37 +00:00
public double IconRadius
{
get
{
if ( Result . RoundedIcon )
{
return IconXY / 2 ;
}
2024-01-18 22:45:49 +00:00
2022-08-30 08:27:37 +00:00
return IconXY ;
}
}
2024-01-18 22:45:49 +00:00
public bool ShowGlyph
2021-10-07 10:58:54 +00:00
{
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 )
2024-01-18 22:45:49 +00:00
return true ;
2021-10-07 10:58:54 +00:00
2024-01-18 22:45:49 +00:00
return Settings . UseGlyphIcons & & GlyphAvailable ;
2021-10-07 10:58:54 +00:00
}
}
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
2024-01-18 22:45:49 +00:00
private bool PreviewImageAvailable = > ! string . IsNullOrEmpty ( Result . Preview . PreviewImagePath ) | |
Result . Preview . PreviewDelegate ! = null ;
2022-12-28 13:37:53 +00:00
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
2024-01-18 23:49:12 +00:00
public Task < Bitmap > Image { get ; set ; }
2021-05-23 04:28:45 +00:00
2024-01-18 23:49:12 +00:00
public Task < Bitmap > PreviewImage { get ; set ; }
2022-09-02 11:40:37 +00:00
2022-11-28 02:29:32 +00:00
/// <summary>
/// Determines if to use the full width of the preview panel
/// </summary>
2022-12-05 08:01:20 +00:00
public bool UseBigThumbnail = > Result . Preview . IsMedia ;
2022-11-25 20:44:08 +00:00
2021-07-31 07:44:41 +00:00
public GlyphInfo Glyph { get ; set ; }
2024-01-18 23:31:04 +00:00
private async Task < Bitmap > LoadImageInternalAsync ( string imagePath , Result . IconDelegate icon ,
2024-01-18 22:45:49 +00:00
bool loadFullImage )
2016-08-18 00:16:40 +00:00
{
2022-11-29 11:26:29 +00:00
if ( string . IsNullOrEmpty ( imagePath ) & & 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
{
2024-01-18 23:31:04 +00:00
// var image = icon();
return default ; // image;
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 (
2022-11-29 11:26:29 +00:00
$"|ResultViewModel.LoadImageInternalAsync|IcoPath is empty and exception when calling IconDelegate for result <{Result.Title}> of plugin <{Result.PluginDirectory}>" ,
2021-08-09 10:38:44 +00:00
e ) ;
2020-11-16 02:12:32 +00:00
}
}
2020-11-15 12:45:00 +00:00
2024-01-19 03:46:21 +00:00
return await ImageLoader . LoadAsync ( imagePath , loadFullImage ) . ConfigureAwait ( false ) ;
2022-11-29 11:26:29 +00:00
}
2022-10-16 09:25:47 +00:00
2024-01-18 23:49:12 +00:00
private void LoadImage ( )
2022-11-29 11:26:29 +00:00
{
var imagePath = Result . IcoPath ;
var iconDelegate = Result . Icon ;
2024-01-18 23:31:04 +00:00
if ( ImageLoader . TryGetValue ( imagePath , false , out var img ) )
2021-05-23 04:28:45 +00:00
{
2024-01-18 23:49:12 +00:00
Image = Task . FromResult ( img ) ;
2022-11-29 11:26:29 +00:00
}
else
{
// We need to modify the property not field here to trigger the OnPropertyChanged event
2024-01-18 23:49:12 +00:00
Image = LoadImageInternalAsync ( imagePath , iconDelegate , false ) ;
2021-05-23 04:28:45 +00:00
}
2016-08-18 00:16:40 +00:00
}
2016-02-19 15:26:13 +00:00
2024-01-18 23:49:12 +00:00
public void LoadPreviewImage ( )
2022-09-02 11:40:37 +00:00
{
2022-12-28 13:45:30 +00:00
var imagePath = Result . Preview . PreviewImagePath ? ? Result . IcoPath ;
var iconDelegate = Result . Preview . PreviewDelegate ? ? Result . Icon ;
2024-01-18 23:31:04 +00:00
if ( ImageLoader . TryGetValue ( imagePath , true , out var img ) )
2022-11-26 06:51:11 +00:00
{
2024-01-18 23:49:12 +00:00
PreviewImage = Task . FromResult ( img ) ;
2022-11-26 06:51:11 +00:00
}
else
{
2022-11-29 11:26:29 +00:00
// We need to modify the property not field here to trigger the OnPropertyChanged event
2024-01-18 23:49:12 +00:00
PreviewImage = LoadImageInternalAsync ( imagePath , iconDelegate , true ) ;
2022-11-26 06:51:11 +00:00
}
2022-09-02 11:40:37 +00:00
}
2022-12-22 16:32:29 +00:00
2016-06-22 23:22:41 +00:00
public Result Result { get ; }
2024-01-18 22:45:49 +00:00
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
}