mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-13 07:46:23 +01:00
Load icons again (#208)
This adds a magic helper to load icons for us. Any time you want an icon, just do this:
```xaml
<Border Width="16"
Height="16"
Margin="4,4,4,4">
<Interactivity:Interaction.Behaviors>
<cmdpalUI:LoadIconBehavior Source="{x:Bind ViewModel.PrimaryAction.Icon, Mode=OneWay}"/>
</Interactivity:Interaction.Behaviors>
</Border>
```
And that'll magically give us a border filled with the icon, and updating with the binding.
I believe it'll also work with `IRandomAccessStreamReference`s, but I didn't actually test that with #151 yet.
I didn't actually implement the "caching" bit of this yet. That'll involve doing some locking per-key inside the factory and I didn't want to futz with that in this initial PR to restore icons
---------
Co-authored-by: Mike Griese <zadjii@gmail.com>
This commit is contained in:
@@ -24,14 +24,20 @@ public partial class ActionBarViewModel : ObservableObject,
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string PrimaryActionName { get; set; } = string.Empty;
|
||||
public partial CommandItemViewModel? PrimaryAction { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string SecondaryActionName { get; set; } = string.Empty;
|
||||
[NotifyPropertyChangedFor(nameof(HasSecondaryCommand))]
|
||||
public partial CommandItemViewModel? SecondaryAction { get; set; }
|
||||
|
||||
public bool HasSecondaryCommand => SecondaryAction != null;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShouldShowContextMenu { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial PageViewModel? CurrentPage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<CommandContextItemViewModel> ContextActions { get; set; } = [];
|
||||
|
||||
@@ -46,8 +52,8 @@ public partial class ActionBarViewModel : ObservableObject,
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
PrimaryActionName = value.Name;
|
||||
SecondaryActionName = value.SecondaryCommandName;
|
||||
PrimaryAction = value;
|
||||
SecondaryAction = value.SecondaryCommand;
|
||||
|
||||
if (value.MoreCommands.Count > 1)
|
||||
{
|
||||
@@ -61,8 +67,8 @@ public partial class ActionBarViewModel : ObservableObject,
|
||||
}
|
||||
else
|
||||
{
|
||||
PrimaryActionName = string.Empty;
|
||||
SecondaryActionName = string.Empty;
|
||||
PrimaryAction = null;
|
||||
SecondaryAction = null;
|
||||
ShouldShowContextMenu = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
|
||||
public string Subtitle { get; private set; } = string.Empty;
|
||||
|
||||
public string IconUri { get; private set; } = string.Empty;
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public ExtensionObject<ICommand> Command { get; private set; } = new(null);
|
||||
|
||||
@@ -36,6 +36,8 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
|
||||
public string SecondaryCommandName => HasMoreCommands ? MoreCommands[0].Name : string.Empty;
|
||||
|
||||
public CommandItemViewModel? SecondaryCommand => HasMoreCommands ? MoreCommands[0] : null;
|
||||
|
||||
public List<CommandContextItemViewModel> AllCommands
|
||||
{
|
||||
get
|
||||
@@ -49,7 +51,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
Name = Name,
|
||||
Title = Name,
|
||||
Subtitle = Subtitle,
|
||||
IconUri = IconUri,
|
||||
Icon = Icon,
|
||||
|
||||
// TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever
|
||||
};
|
||||
@@ -79,7 +81,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
Name = model.Command?.Name ?? string.Empty;
|
||||
Title = model.Title;
|
||||
Subtitle = model.Subtitle;
|
||||
IconUri = model.Icon.Icon;
|
||||
Icon = model.Icon;
|
||||
MoreCommands = model.MoreCommands
|
||||
.Where(contextItem => contextItem is ICommandContextItem)
|
||||
.Select(contextItem => (contextItem as ICommandContextItem)!)
|
||||
@@ -129,6 +131,9 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
case nameof(Subtitle):
|
||||
this.Subtitle = model.Subtitle;
|
||||
break;
|
||||
case nameof(Icon):
|
||||
this.Icon = model.Icon;
|
||||
break;
|
||||
|
||||
// TODO! Icon
|
||||
// TODO! MoreCommands array, which needs to also raise HasMoreCommands
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class DetailsPaneViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial DetailsViewModel? Details { get; set; }
|
||||
|
||||
public DetailsPaneViewModel()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,10 @@ public partial class DetailsViewModel(IDetails _details, TaskScheduler Scheduler
|
||||
|
||||
// Remember - "observable" properties from the model (via PropChanged)
|
||||
// cannot be marked [ObservableProperty]
|
||||
public IconDataType HeroImage { get; private set; } = new(string.Empty);
|
||||
|
||||
public bool HasHeroImage => !string.IsNullOrEmpty(HeroImage.Icon) || HeroImage.Data != null;
|
||||
|
||||
// TODO: Icon
|
||||
// TODO: Metadata is an array of IDetailsElement,
|
||||
// where IDetailsElement = {IDetailsTags, IDetailsLink, IDetailsSeparator}
|
||||
public string Title { get; private set; } = string.Empty;
|
||||
@@ -31,9 +33,12 @@ public partial class DetailsViewModel(IDetails _details, TaskScheduler Scheduler
|
||||
|
||||
Title = model.Title;
|
||||
Body = model.Body;
|
||||
HeroImage = model.HeroImage;
|
||||
|
||||
UpdateProperty(nameof(Title));
|
||||
UpdateProperty(nameof(Body));
|
||||
UpdateProperty(nameof(HeroImage));
|
||||
UpdateProperty(nameof(HasHeroImage));
|
||||
}
|
||||
|
||||
protected void UpdateProperty(string propertyName) => Task.Factory.StartNew(() => { OnPropertyChanged(propertyName); }, CancellationToken.None, TaskCreationOptions.None, Scheduler);
|
||||
|
||||
@@ -37,6 +37,8 @@ public partial class PageViewModel : ExtensionObjectViewModel
|
||||
|
||||
public bool IsLoading { get; private set; } = true;
|
||||
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public PageViewModel(IPage model, TaskScheduler scheduler)
|
||||
{
|
||||
_pageModel = new(model);
|
||||
@@ -74,10 +76,12 @@ public partial class PageViewModel : ExtensionObjectViewModel
|
||||
|
||||
Name = page.Name;
|
||||
IsLoading = page.IsLoading;
|
||||
Icon = page.Icon;
|
||||
|
||||
// Let the UI know about our initial properties too.
|
||||
UpdateProperty(nameof(Name));
|
||||
UpdateProperty(nameof(IsLoading));
|
||||
UpdateProperty(nameof(Icon));
|
||||
|
||||
page.PropChanged += Model_PropChanged;
|
||||
}
|
||||
@@ -119,6 +123,9 @@ public partial class PageViewModel : ExtensionObjectViewModel
|
||||
case nameof(IsLoading):
|
||||
this.IsLoading = model.IsLoading;
|
||||
break;
|
||||
case nameof(Icon):
|
||||
this.Icon = model.Icon;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateProperty(propertyName);
|
||||
|
||||
@@ -19,7 +19,10 @@ public partial class TagViewModel(ITag _tag, TaskScheduler Scheduler) : Extensio
|
||||
|
||||
public OptionalColor Color { get; private set; }
|
||||
|
||||
// TODO Icon
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public bool HasIcon => !string.IsNullOrEmpty(Icon.Icon);
|
||||
|
||||
public ExtensionObject<ICommand> Command { get; private set; } = new(null);
|
||||
|
||||
public override void InitializeProperties()
|
||||
@@ -34,10 +37,12 @@ public partial class TagViewModel(ITag _tag, TaskScheduler Scheduler) : Extensio
|
||||
Text = model.Text;
|
||||
Color = model.Color;
|
||||
Tooltip = model.ToolTip;
|
||||
Icon = model.Icon;
|
||||
|
||||
UpdateProperty(nameof(Text));
|
||||
UpdateProperty(nameof(Color));
|
||||
UpdateProperty(nameof(Tooltip));
|
||||
UpdateProperty(nameof(Icon));
|
||||
}
|
||||
|
||||
protected void UpdateProperty(string propertyName) => Task.Factory.StartNew(() => { OnPropertyChanged(propertyName); }, CancellationToken.None, TaskCreationOptions.None, Scheduler);
|
||||
|
||||
@@ -12,7 +12,6 @@ using Microsoft.CmdPal.Ext.WindowsServices;
|
||||
using Microsoft.CmdPal.Ext.WindowsSettings;
|
||||
using Microsoft.CmdPal.Ext.WindowsTerminal;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.UI.Pages;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:cmdpalUI="using:Microsoft.CmdPal.UI"
|
||||
xmlns:local="using:Microsoft.CmdPal.UI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:viewmodels="using:Microsoft.CmdPal.UI.ViewModels"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
Background="Transparent"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -24,19 +26,21 @@
|
||||
<ListViewItem KeyDown="ActionListViewItem_KeyDown" Tapped="ActionListViewItem_Tapped">
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="24" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Viewbox Width="16" Height="16">
|
||||
<!-- TODO bind to icon -->
|
||||
<ContentControl
|
||||
<Border x:Name="IconBorder"
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24">
|
||||
<!--<SymbolIcon Symbol="Emoji" />-->
|
||||
</ContentControl>
|
||||
</Viewbox>
|
||||
Width="16"
|
||||
Height="16"
|
||||
Margin="0,0,0,0">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<cmdpalUI:LoadIconBehavior Source="{x:Bind Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{x:Bind Title}" />
|
||||
</Grid>
|
||||
</ListViewItem>
|
||||
@@ -51,13 +55,17 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- TO DO: Placeholder, needs to be replaced with extension info -->
|
||||
<Border
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="12,0,0,0"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}" />
|
||||
|
||||
<Border x:Name="IconBorder"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="12,0,0,0"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<cmdpalUI:LoadIconBehavior Source="{x:Bind ViewModel.CurrentPage.Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
@@ -73,15 +81,24 @@
|
||||
<Button
|
||||
Height="40"
|
||||
Padding="8,4,8,4"
|
||||
Visibility="{x:Bind ViewModel.PrimaryActionName, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}">
|
||||
Visibility="{x:Bind ViewModel.PrimaryAction.Name, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}">
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon Glyph="" />
|
||||
<!-- <FontIcon Glyph="" /> -->
|
||||
<Border Width="16"
|
||||
Height="16"
|
||||
Margin="4,4,4,4">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<cmdpalUI:LoadIconBehavior Source="{x:Bind ViewModel.PrimaryAction.Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<StackPanel Orientation="Vertical" Spacing="2">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.PrimaryActionName, Mode=OneWay}" />
|
||||
Text="{x:Bind ViewModel.PrimaryAction.Name, Mode=OneWay}" />
|
||||
<FontIcon
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
@@ -94,14 +111,24 @@
|
||||
<Button
|
||||
Height="40"
|
||||
Padding="8,4,8,4"
|
||||
Visibility="{x:Bind ViewModel.SecondaryActionName, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}">
|
||||
Visibility="{x:Bind ViewModel.HasSecondaryCommand, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon Glyph="" />
|
||||
<!-- <FontIcon Glyph="" /> -->
|
||||
|
||||
<Border Width="16"
|
||||
Height="16"
|
||||
Margin="4,4,4,4">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<cmdpalUI:LoadIconBehavior Source="{x:Bind ViewModel.SecondaryAction.Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<StackPanel Orientation="Vertical" Spacing="1">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SecondaryActionName, Mode=OneWay}" />
|
||||
Text="{x:Bind ViewModel.SecondaryAction.Name, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
|
||||
@@ -29,11 +29,13 @@ public sealed partial class ActionBar : UserControl, ICurrentPageAware
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS has a tendency to delete XAML bound methods over-agressively")]
|
||||
private void ActionListViewItem_KeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS has a tendency to delete XAML bound methods over-agressively")]
|
||||
private void ActionListViewItem_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
MoreCommandsButton.Flyout.Hide();
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.Terminal.UI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ExtViews;
|
||||
|
||||
public sealed class IconCacheService(DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
public Task<IconSource?> GetIconSource(IconDataType icon) =>
|
||||
|
||||
// todo: actually implement a cache of some sort
|
||||
IconToSource(icon);
|
||||
|
||||
private async Task<IconSource?> IconToSource(IconDataType icon)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(icon.Icon))
|
||||
{
|
||||
var source = IconPathConverter.IconSourceMUX(icon.Icon, false);
|
||||
|
||||
return source;
|
||||
}
|
||||
else if (icon.Data != null)
|
||||
{
|
||||
return await StreamToIconSource(icon.Data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<IconSource?> StreamToIconSource(IRandomAccessStreamReference iconStreamRef)
|
||||
{
|
||||
if (iconStreamRef == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var bitmap = await IconStreamToBitmapImageAsync(iconStreamRef);
|
||||
var icon = new ImageIconSource() { ImageSource = bitmap };
|
||||
return icon;
|
||||
}
|
||||
|
||||
private async Task<BitmapImage> IconStreamToBitmapImageAsync(IRandomAccessStreamReference iconStreamRef)
|
||||
{
|
||||
// Return the bitmap image via TaskCompletionSource. Using WCT's EnqueueAsync does not suffice here, since if
|
||||
// we're already on the thread of the DispatcherQueue then it just directly calls the function, with no async involved.
|
||||
var completionSource = new TaskCompletionSource<BitmapImage>();
|
||||
dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
using var bitmapStream = await iconStreamRef.OpenReadAsync();
|
||||
var itemImage = new BitmapImage();
|
||||
await itemImage.SetSourceAsync(bitmapStream);
|
||||
completionSource.TrySetResult(itemImage);
|
||||
});
|
||||
|
||||
var bitmapImage = await completionSource.Task;
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
}
|
||||
@@ -34,26 +34,29 @@
|
||||
|
||||
<DataTemplate x:Key="TagTemplate" x:DataType="viewmodels:TagViewModel">
|
||||
<!-- TODO: Actually colorize the tags again -->
|
||||
<Border
|
||||
<StackPanel
|
||||
Padding="4,2,4,2"
|
||||
VerticalAlignment="Center"
|
||||
BorderBrush="{ThemeResource TextBoxBorderThemeBrush}"
|
||||
BorderThickness="1"
|
||||
Orientation="Horizontal"
|
||||
CornerRadius="4">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!-- -TODO: Icon
|
||||
<ContentControl
|
||||
<Border x:Name="IconBorder"
|
||||
Width="12"
|
||||
Height="12"
|
||||
Margin="0,0,4,0"
|
||||
Content="{x:Bind IcoElement, Mode=OneWay}"
|
||||
Visibility="{x:Bind HasIcon, Mode=OneWay}" /> -->
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind Text, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
Visibility="{x:Bind HasIcon, Mode=OneWay}">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<local:LoadIconBehavior Source="{x:Bind Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind Text, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- https://learn.microsoft.com/windows/apps/design/controls/itemsview#specify-the-look-of-the-items -->
|
||||
@@ -68,14 +71,17 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Viewbox
|
||||
Width="20"
|
||||
Height="20"
|
||||
VerticalAlignment="Center">
|
||||
<!--TODO: Icon
|
||||
<ContentControl Content="{x:Bind IcoElement, Mode=OneWay}" />
|
||||
-->
|
||||
</Viewbox>
|
||||
|
||||
<Border x:Name="IconBorder"
|
||||
Grid.Column="0"
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="4,0,4,0">
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<local:LoadIconBehavior Source="{x:Bind Icon, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.UI.ExtViews;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Xaml.Interactivity;
|
||||
|
||||
namespace Microsoft.CmdPal.UI;
|
||||
|
||||
public partial class LoadIconBehavior : DependencyObject, IBehavior
|
||||
{
|
||||
private static readonly IconCacheService IconService = new(Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
|
||||
|
||||
public IconDataType Source
|
||||
{
|
||||
get => (IconDataType)GetValue(SourceProperty);
|
||||
set
|
||||
{
|
||||
SetValue(SourceProperty, value);
|
||||
OnSourcePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// Using a DependencyProperty as the backing store for Source. This enables animation, styling, binding, etc...
|
||||
public static readonly DependencyProperty SourceProperty =
|
||||
DependencyProperty.Register(nameof(Source), typeof(IconDataType), typeof(LoadIconBehavior), new PropertyMetadata(new IconDataType(string.Empty)));
|
||||
|
||||
public DependencyObject? AssociatedObject { get; private set; }
|
||||
|
||||
public void Attach(DependencyObject associatedObject) => AssociatedObject = associatedObject;
|
||||
|
||||
public void Detach() => AssociatedObject = null;
|
||||
|
||||
public async void OnSourcePropertyChanged()
|
||||
{
|
||||
var icoSource = await IconService.GetIconSource(Source ?? new(string.Empty));
|
||||
|
||||
if (AssociatedObject is Border border)
|
||||
{
|
||||
if (icoSource is FontIconSource fontIco)
|
||||
{
|
||||
fontIco.FontSize = border.Width;
|
||||
|
||||
// For inexplicable reasons, FontIconSource.CreateIconElement
|
||||
// doesn't work, so do it ourselves
|
||||
IconSourceElement elem = new()
|
||||
{
|
||||
IconSource = fontIco,
|
||||
};
|
||||
border.Child = elem;
|
||||
}
|
||||
else
|
||||
{
|
||||
var icoElement = icoSource?.CreateIconElement();
|
||||
border.Child = icoElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,13 @@
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- This lets us actually reference types from Microsoft.Terminal.UI -->
|
||||
<CsWinRTIncludes>Microsoft.Terminal.UI</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- This disables the auto-generated main, so we can be single-instanced -->
|
||||
<DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:labs="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
|
||||
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
Background="Transparent"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -84,11 +85,14 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border
|
||||
Width="64"
|
||||
Height="64"
|
||||
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource ControlCornerRadius}">
|
||||
<Border x:Name="HeroImageBorder"
|
||||
Width="64"
|
||||
Height="64"
|
||||
Visibility="{x:Bind ViewModel.Details.HasHeroImage, Mode=OneWay}" >
|
||||
<!-- LoadIconBehavior will magically fill this border up with an icon -->
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<local:LoadIconBehavior Source="{x:Bind ViewModel.Details.HeroImage, Mode=OneWay}"/>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
|
||||
@@ -54,6 +54,7 @@ public sealed partial class ShellPage :
|
||||
if (RootFrame.CanGoBack)
|
||||
{
|
||||
RootFrame.GoBack();
|
||||
HideDetails();
|
||||
RootFrame.ForwardStack.Clear();
|
||||
SearchBox.Focus(Microsoft.UI.Xaml.FocusState.Programmatic);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user