IconDataViewModel.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using CommunityToolkit.Mvvm.ComponentModel; 6 using Microsoft.CmdPal.Core.ViewModels.Models; 7 using Microsoft.CommandPalette.Extensions; 8 using Microsoft.CommandPalette.Extensions.Toolkit; 9 using Windows.Storage.Streams; 10 11 namespace Microsoft.CmdPal.Core.ViewModels; 12 13 public partial class IconDataViewModel : ObservableObject, IIconData 14 { 15 private readonly ExtensionObject<IIconData> _model = new(null); 16 17 // If the extension previously gave us a Data, then died, the data will 18 // throw if we actually try to read it, but the pointer itself won't be 19 // null, so this is relatively safe. 20 public bool HasIcon => !string.IsNullOrEmpty(Icon) || Data.Unsafe is not null; 21 22 // Locally cached properties from IIconData. 23 public string Icon { get; private set; } = string.Empty; 24 25 // Streams are not trivially copy-able, so we can't copy the data locally 26 // first. Hence why we're sticking this into an ExtensionObject 27 public ExtensionObject<IRandomAccessStreamReference> Data { get; private set; } = new(null); 28 29 IRandomAccessStreamReference? IIconData.Data => Data.Unsafe; 30 31 public string? FontFamily { get; private set; } 32 33 public IconDataViewModel(IIconData? icon) 34 { 35 _model = new(icon); 36 } 37 38 // Unsafe, needs to be called on BG thread 39 public void InitializeProperties() 40 { 41 var model = _model.Unsafe; 42 if (model is null) 43 { 44 return; 45 } 46 47 Icon = model.Icon; 48 Data = new(model.Data); 49 50 if (model is IExtendedAttributesProvider icon2) 51 { 52 var props = icon2.GetProperties(); 53 54 // From Raymond Chen: 55 // Make sure you don't try do do something like 56 // icon2.GetProperties().TryGetValue("awesomeKey", out var awesomeValue); 57 // icon2.GetProperties().TryGetValue("slackerKey", out var slackerValue); 58 // because each call to GetProperties() is a cross process hop, and if you 59 // marshal-by-value the property set, then you don't want to throw it away and 60 // re-marshal it for every property. MAKE SURE YOU CACHE IT. 61 if (props?.TryGetValue(WellKnownExtensionAttributes.FontFamily, out var family) ?? false) 62 { 63 FontFamily = family as string; 64 } 65 } 66 } 67 }