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 ;
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 ;
}
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 ) )
{
Glyph = glyph with
2021-12-27 02:22:52 +00:00
{
2022-11-25 20:44:08 +00:00
FontFamily = fonts [ fontFamilyPath ]
} ;
2021-08-06 09:15:10 +00:00
}
else
{
2022-11-25 20:44:08 +00:00
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
}
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
}
2020-05-03 21:36:23 +00:00
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 ;
2022-11-26 06:33:42 +00:00
public Visibility ShowCustomizedPreview = > Result . PreviewPanel = = null ? Visibility . Collapsed : Visibility . Visible ;
2022-09-13 22:34:11 +00:00
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-12-28 13:37:53 +00:00
public bool ShowPreviewImage
{
get
{
if ( ! string . IsNullOrEmpty ( Result . Preview . PreviewImagePath ) | | Result . Preview . PreviewDelegate ! = null )
{
return true ;
}
else
{
// Fall back to Icon
if ( ! ImgIconAvailable & & ! GlyphAvailable )
return true ;
// 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 true ;
return ! Settings . UseGlyphIcons & & ImgIconAvailable ;
}
}
}
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 ;
2022-12-05 22:49:21 +00:00
return Settings . UseGlyphIcons & & GlyphAvailable ? Visibility . Visible : Visibility . Collapsed ;
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
2022-12-28 13:37:53 +00:00
private bool PreviewImageAvailable = > ! string . IsNullOrEmpty ( Result . Preview . PreviewImagePath ) | | Result . Preview . PreviewDelegate ! = 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
2022-11-26 16:16:04 +00:00
private ImageSource image = ImageLoader . LoadingImage ;
2022-11-26 16:29:04 +00:00
private ImageSource previewImage = ImageLoader . LoadingImage ;
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
{
2022-12-22 16:32:29 +00:00
get = > previewImage ;
2022-09-02 11:40:37 +00:00
private set = > previewImage = value ;
}
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 ; }
2022-11-29 11:26:29 +00:00
private async Task < ImageSource > LoadImageInternalAsync ( string imagePath , Result . IconDelegate icon , 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
{
2022-11-29 11:26:29 +00:00
var image = await Task . Run ( ( ) = > icon ( ) ) . ConfigureAwait ( false ) ;
return 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
2022-11-29 11:26:29 +00:00
return await ImageLoader . LoadAsync ( imagePath , loadFullImage ) . ConfigureAwait ( false ) ;
}
2022-10-16 09:25:47 +00:00
2022-11-29 11:26:29 +00:00
private async Task LoadImageAsync ( )
{
var imagePath = Result . IcoPath ;
var iconDelegate = Result . Icon ;
2022-12-29 03:43:40 +00:00
if ( ImageLoader . TryGetValue ( imagePath , false , out ImageSource img ) )
2021-05-23 04:28:45 +00:00
{
2022-12-29 03:43:40 +00:00
image = img ;
2022-11-29 11:26:29 +00:00
}
else
{
// We need to modify the property not field here to trigger the OnPropertyChanged event
Image = await LoadImageInternalAsync ( imagePath , iconDelegate , false ) . ConfigureAwait ( 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
2022-11-25 19:51:07 +00:00
private async Task LoadPreviewImageAsync ( )
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 ;
2022-12-29 03:43:40 +00:00
if ( ImageLoader . TryGetValue ( imagePath , true , out ImageSource img ) )
2022-11-26 06:51:11 +00:00
{
2022-12-29 03:43:40 +00:00
previewImage = 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
PreviewImage = await LoadImageInternalAsync ( imagePath , iconDelegate , true ) . ConfigureAwait ( false ) ;
2022-11-26 06:51:11 +00:00
}
2022-09-02 11:40:37 +00:00
}
2022-12-22 16:32:29 +00:00
public void LoadPreviewImage ( )
{
2022-12-27 03:53:10 +00:00
if ( ShowDefaultPreview = = Visibility . Visible )
2022-12-22 16:32:29 +00:00
{
2022-12-28 13:37:53 +00:00
if ( ! PreviewImageLoaded & & ShowPreviewImage )
2022-12-27 03:53:10 +00:00
{
PreviewImageLoaded = true ;
_ = LoadPreviewImageAsync ( ) ;
}
2022-12-22 16:32:29 +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
}