mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
Allow both light and dark icons (#286)
This adds one _last_ change to the API to allow apps to specify different icons for light and dark mode. If it's important to an app to specify different icons for light vs dark mode, then `IconInfo` is exactly what you want to use. It contains _two_ `IconDataType`s, for two different icons. Simple as that. And to keep things even easier, I slapped on a `IconInfo(string)` constructor, so that you can easily build a `IconInfo` with both icons set to the same string. Especially useful for font icons, which we're using everywhere already. That allows almost all the extensions to have _no code change_ here, so that's super! Some of the places where we were evaluating if an icon existed or not - that needs to move into the view. `ShellPage.xaml.cs` does one variant of that already for `IDetails.HeroImage`. The view is the only part of the app that knows what the theme is. Closes #78
This commit is contained in:
@@ -64,7 +64,7 @@ public static class ServiceHelper
|
||||
];
|
||||
}
|
||||
|
||||
IconDataType icon = new("\U0001f7e2"); // unicode LARGE GREEN CIRCLE
|
||||
IconInfo icon = new("\U0001f7e2"); // unicode LARGE GREEN CIRCLE
|
||||
switch (s.Status)
|
||||
{
|
||||
case ServiceControllerStatus.Stopped:
|
||||
@@ -191,15 +191,13 @@ public static class ServiceHelper
|
||||
{
|
||||
return Resources.wox_plugin_service_running;
|
||||
}
|
||||
else if (status == ServiceControllerStatus.ContinuePending)
|
||||
{
|
||||
return Resources.wox_plugin_service_continue_pending;
|
||||
}
|
||||
else
|
||||
{
|
||||
return status == ServiceControllerStatus.PausePending
|
||||
? Resources.wox_plugin_service_pause_pending
|
||||
: status == ServiceControllerStatus.Paused ? Resources.wox_plugin_service_paused : status.ToString();
|
||||
return status == ServiceControllerStatus.ContinuePending
|
||||
? Resources.wox_plugin_service_continue_pending
|
||||
: status == ServiceControllerStatus.PausePending
|
||||
? Resources.wox_plugin_service_pause_pending
|
||||
: status == ServiceControllerStatus.Paused ? Resources.wox_plugin_service_paused : status.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,44 +211,32 @@ public static class ServiceHelper
|
||||
{
|
||||
return Resources.wox_plugin_service_start_mode_system;
|
||||
}
|
||||
else if (startMode == ServiceStartMode.Automatic)
|
||||
{
|
||||
return !IsDelayedStart(serviceName) ? Resources.wox_plugin_service_start_mode_automatic : Resources.wox_plugin_service_start_mode_automaticDelayed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return startMode == ServiceStartMode.Manual
|
||||
? Resources.wox_plugin_service_start_mode_manual
|
||||
: startMode == ServiceStartMode.Disabled ? Resources.wox_plugin_service_start_mode_disabled : startMode.ToString();
|
||||
return startMode == ServiceStartMode.Automatic
|
||||
? !IsDelayedStart(serviceName) ? Resources.wox_plugin_service_start_mode_automatic : Resources.wox_plugin_service_start_mode_automaticDelayed
|
||||
: startMode == ServiceStartMode.Manual
|
||||
? Resources.wox_plugin_service_start_mode_manual
|
||||
: startMode == ServiceStartMode.Disabled ? Resources.wox_plugin_service_start_mode_disabled : startMode.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLocalizedMessage(Action action)
|
||||
{
|
||||
if (action == Action.Start)
|
||||
{
|
||||
return Resources.wox_plugin_service_started_notification;
|
||||
}
|
||||
else
|
||||
{
|
||||
return action == Action.Stop
|
||||
return action == Action.Start
|
||||
? Resources.wox_plugin_service_started_notification
|
||||
: action == Action.Stop
|
||||
? Resources.wox_plugin_service_stopped_notification
|
||||
: action == Action.Restart ? Resources.wox_plugin_service_restarted_notification : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetLocalizedErrorMessage(Action action)
|
||||
{
|
||||
if (action == Action.Start)
|
||||
{
|
||||
return Resources.wox_plugin_service_start_error_notification;
|
||||
}
|
||||
else
|
||||
{
|
||||
return action == Action.Stop
|
||||
return action == Action.Start
|
||||
? Resources.wox_plugin_service_start_error_notification
|
||||
: action == Action.Stop
|
||||
? Resources.wox_plugin_service_stop_error_notification
|
||||
: action == Action.Restart ? Resources.wox_plugin_service_restart_error_notification : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDelayedStart(string serviceName)
|
||||
|
||||
@@ -25,7 +25,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
|
||||
public string Subtitle { get; private set; } = string.Empty;
|
||||
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
public IconInfo Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public ExtensionObject<ICommand> Command { get; private set; } = new(null);
|
||||
|
||||
@@ -81,9 +81,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
Subtitle = model.Subtitle;
|
||||
|
||||
var listIcon = model.Icon;
|
||||
Icon = !string.IsNullOrEmpty(listIcon.Icon) ?
|
||||
listIcon :
|
||||
Command.Unsafe!.Icon;
|
||||
Icon = listIcon ?? Command.Unsafe!.Icon;
|
||||
|
||||
var more = model.MoreCommands;
|
||||
if (more != null)
|
||||
@@ -171,7 +169,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel
|
||||
break;
|
||||
case nameof(Icon):
|
||||
var listIcon = model.Icon;
|
||||
Icon = !string.IsNullOrEmpty(listIcon.Icon) ? listIcon : Command.Unsafe!.Icon;
|
||||
Icon = listIcon != null ? listIcon : Command.Unsafe!.Icon;
|
||||
break;
|
||||
|
||||
// TODO! MoreCommands array, which needs to also raise HasMoreCommands
|
||||
|
||||
@@ -28,7 +28,7 @@ public sealed class CommandProviderWrapper
|
||||
|
||||
public string DisplayName { get; private set; } = string.Empty;
|
||||
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
public IconInfo Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public string ProviderId => $"{extensionWrapper?.PackageFamilyName ?? string.Empty}/{Id}";
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ public partial class DetailsViewModel(IDetails _details, IPageContext context) :
|
||||
|
||||
// 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;
|
||||
public IconInfo HeroImage { get; private set; } = new(string.Empty);
|
||||
|
||||
// TODO: Metadata is an array of IDetailsElement,
|
||||
// where IDetailsElement = {IDetailsTags, IDetailsLink, IDetailsSeparator}
|
||||
@@ -38,6 +36,5 @@ public partial class DetailsViewModel(IDetails _details, IPageContext context) :
|
||||
UpdateProperty(nameof(Title));
|
||||
UpdateProperty(nameof(Body));
|
||||
UpdateProperty(nameof(HeroImage));
|
||||
UpdateProperty(nameof(HasHeroImage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext
|
||||
// `IsLoading` property as a combo of this value and `IsInitialized`
|
||||
public bool ModelIsLoading { get; protected set; } = true;
|
||||
|
||||
public IconDataType Icon { get; protected set; } = new(string.Empty);
|
||||
public IconInfo Icon { get; protected set; } = new(string.Empty);
|
||||
|
||||
public PageViewModel(IPage? model, TaskScheduler scheduler)
|
||||
: base(null)
|
||||
|
||||
@@ -13,7 +13,7 @@ public partial class ProviderSettingsViewModel(CommandProviderWrapper _provider,
|
||||
|
||||
public string ExtensionName => _provider.Extension?.ExtensionDisplayName ?? "Built-in";
|
||||
|
||||
public IconDataType Icon => _provider.Icon;
|
||||
public IconInfo Icon => _provider.Icon;
|
||||
|
||||
public bool IsEnabled
|
||||
{
|
||||
|
||||
@@ -21,9 +21,10 @@ public partial class TagViewModel(ITag _tag, IPageContext context) : ExtensionOb
|
||||
|
||||
public OptionalColor Background { get; private set; }
|
||||
|
||||
public IconDataType Icon { get; private set; } = new(string.Empty);
|
||||
public IconInfo Icon { get; private set; } = new(string.Empty);
|
||||
|
||||
public bool HasIcon => !string.IsNullOrEmpty(Icon.Icon);
|
||||
// TODO Terrible. When we redo the icons in tags, make this something the view exposes
|
||||
public bool HasIcon => !string.IsNullOrEmpty(Icon.Light.Icon);
|
||||
|
||||
public ExtensionObject<ICommand> Command { get; private set; } = new(null);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial class TopLevelCommandWrapper : ListItem
|
||||
|
||||
Title = model.Title;
|
||||
Subtitle = model.Subtitle;
|
||||
Icon = new(model.Icon.Icon);
|
||||
Icon = model.Icon;
|
||||
MoreCommands = model.MoreCommands;
|
||||
|
||||
model.PropChanged += Model_PropChanged;
|
||||
|
||||
@@ -92,7 +92,8 @@ public partial class IconBox : ContentControl
|
||||
// _ = @this._queue.EnqueueAsync(() =>
|
||||
@this._queue.TryEnqueue(new(() =>
|
||||
{
|
||||
var eventArgs = new SourceRequestedEventArgs(e.NewValue);
|
||||
var requestedTheme = @this.ActualTheme;
|
||||
var eventArgs = new SourceRequestedEventArgs(e.NewValue, requestedTheme);
|
||||
|
||||
if (@this.SourceRequested != null)
|
||||
{
|
||||
@@ -110,7 +111,7 @@ public partial class IconBox : ContentControl
|
||||
// Segoe icons, then let's give the icon some extra space
|
||||
@this.Padding = new Thickness(0);
|
||||
|
||||
if (eventArgs.Key is IconDataType iconData &&
|
||||
if (eventArgs.Key is IconData iconData &&
|
||||
@this.Source is FontIconSource)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(iconData.Icon) && iconData.Icon.Length <= 2)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using CommunityToolkit.Common.Deferred;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Controls;
|
||||
@@ -10,9 +11,11 @@ namespace Microsoft.CmdPal.UI.Controls;
|
||||
/// <summary>
|
||||
/// See <see cref="IconBox.SourceRequested"/> event.
|
||||
/// </summary>
|
||||
public class SourceRequestedEventArgs(object? key) : DeferredEventArgs
|
||||
public class SourceRequestedEventArgs(object? key, ElementTheme requestedTheme) : DeferredEventArgs
|
||||
{
|
||||
public object? Key { get; private set; } = key;
|
||||
|
||||
public IconSource? Value { get; set; }
|
||||
|
||||
public ElementTheme Theme => requestedTheme;
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Microsoft.CmdPal.UI.ExtViews;
|
||||
|
||||
public sealed class IconCacheService(DispatcherQueue dispatcherQueue)
|
||||
{
|
||||
public Task<IconSource?> GetIconSource(IconDataType icon) =>
|
||||
public Task<IconSource?> GetIconSource(IconData icon) =>
|
||||
|
||||
// todo: actually implement a cache of some sort
|
||||
IconToSource(icon);
|
||||
|
||||
private async Task<IconSource?> IconToSource(IconDataType icon)
|
||||
private async Task<IconSource?> IconToSource(IconData icon)
|
||||
{
|
||||
// bodgy: apparently IconDataType, despite being a struct, doesn't get
|
||||
// bodgy: apparently IconData, despite being a struct, doesn't get
|
||||
// MarshalByValue'd into our process. What's even the point then?
|
||||
try
|
||||
{
|
||||
|
||||
@@ -24,12 +24,21 @@ public static partial class IconCacheProvider
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Key is IconDataType iconData)
|
||||
if (args.Key is IconData iconData)
|
||||
{
|
||||
var deferral = args.GetDeferral();
|
||||
|
||||
args.Value = await IconService.GetIconSource(iconData);
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
else if (args.Key is IconInfo iconInfo)
|
||||
{
|
||||
var deferral = args.GetDeferral();
|
||||
|
||||
var data = args.Theme == Microsoft.UI.Xaml.ElementTheme.Dark ? iconInfo.Dark : iconInfo.Light;
|
||||
args.Value = await IconService.GetIconSource(data);
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,10 +168,9 @@
|
||||
x:Name="HeroImageBorder"
|
||||
Width="64"
|
||||
Height="64"
|
||||
x:Load="{x:Bind IsLoaded, Mode=OneWay}"
|
||||
SourceKey="{x:Bind ViewModel.Details.HeroImage, Mode=OneWay}"
|
||||
SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}"
|
||||
Visibility="{x:Bind ViewModel.Details.HasHeroImage, Mode=OneWay}" />
|
||||
Visibility="{x:Bind HasHeroImage, Mode=OneWay}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
@@ -27,7 +28,8 @@ public sealed partial class ShellPage :
|
||||
IRecipient<HideDetailsMessage>,
|
||||
IRecipient<ClearSearchMessage>,
|
||||
IRecipient<HandleCommandResultMessage>,
|
||||
IRecipient<LaunchUriMessage>
|
||||
IRecipient<LaunchUriMessage>,
|
||||
INotifyPropertyChanged
|
||||
{
|
||||
private readonly DispatcherQueue _queue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
@@ -37,7 +39,8 @@ public sealed partial class ShellPage :
|
||||
|
||||
public ShellViewModel ViewModel { get; private set; } = App.Current.Services.GetService<ShellViewModel>()!;
|
||||
|
||||
// private readonly SettingsViewModel _settingsViewModel;
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public ShellPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -229,6 +232,10 @@ public sealed partial class ShellPage :
|
||||
{
|
||||
ViewModel.Details = message.Details;
|
||||
ViewModel.IsDetailsVisible = true;
|
||||
|
||||
// Trigger a re-evaluation of whether we have a hero image based on
|
||||
// the current theme
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasHeroImage)));
|
||||
}
|
||||
|
||||
public void Receive(HideDetailsMessage message) => HideDetails();
|
||||
@@ -285,4 +292,29 @@ public sealed partial class ShellPage :
|
||||
ViewModel.CurrentPage = page;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether determines if the current Details have a HeroImage, given the theme
|
||||
/// we're currently in. This needs to be evaluated in the view, because the
|
||||
/// viewModel doesn't actually know what the current theme is.
|
||||
/// </summary>
|
||||
public bool HasHeroImage
|
||||
{
|
||||
get
|
||||
{
|
||||
var requestedTheme = ActualTheme;
|
||||
var iconInfo = ViewModel.Details?.HeroImage;
|
||||
var data = requestedTheme == Microsoft.UI.Xaml.ElementTheme.Dark ?
|
||||
iconInfo?.Dark :
|
||||
iconInfo?.Light;
|
||||
|
||||
// We have an icon, AND EITHER:
|
||||
// We have a string icon, OR
|
||||
// we have a data blob
|
||||
var hasIcon = (data != null) &&
|
||||
(!string.IsNullOrEmpty(data.Icon) ||
|
||||
data.Data != null);
|
||||
return hasIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ public sealed class ActionViewModel(ICommand cmd)
|
||||
|
||||
internal bool CanInvoke => cmd is IInvokableCommand;
|
||||
|
||||
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(Command.Icon.Icon);
|
||||
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(Command.Icon.Dark.Icon);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class ContextItemViewModel : INotifyPropertyChanged
|
||||
|
||||
internal string Name => Command.Name;
|
||||
|
||||
internal IconDataType Icon => Command.Icon;
|
||||
internal IconData Icon => Command.Icon.Dark;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ public sealed class ListItemViewModel : INotifyPropertyChanged, IDisposable, IEq
|
||||
this.ListItem = new(model);
|
||||
this.Title = model.Title;
|
||||
this.Subtitle = model.Subtitle;
|
||||
this.Icon = model.Icon.Icon;
|
||||
this.Icon = model.Icon.Dark.Icon;
|
||||
this.TextToSuggest = model.TextToSuggest;
|
||||
|
||||
if (model.Tags != null)
|
||||
@@ -151,7 +151,7 @@ public sealed class ListItemViewModel : INotifyPropertyChanged, IDisposable, IEq
|
||||
BubbleXamlPropertyChanged(nameof(ContextActions));
|
||||
break;
|
||||
case nameof(Icon):
|
||||
this.Icon = item.Command.Icon.Icon;
|
||||
this.Icon = item.Command.Icon.Dark.Icon;
|
||||
BubbleXamlPropertyChanged(nameof(IcoElement));
|
||||
break;
|
||||
case nameof(TextToSuggest):
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.CmdPalSettings\Microsoft.CmdPal.Ext.Settings.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj" />
|
||||
<ProjectReference Include="..\Exts\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj" />
|
||||
|
||||
@@ -12,13 +12,13 @@ public sealed class TagViewModel
|
||||
{
|
||||
private readonly ITag _tag;
|
||||
|
||||
internal IconDataType Icon => _tag.Icon;
|
||||
internal IconInfo Icon => _tag.Icon;
|
||||
|
||||
internal string Text => _tag.Text;
|
||||
|
||||
public bool HasIcon => !string.IsNullOrEmpty(Icon?.Icon);
|
||||
public bool HasIcon => !string.IsNullOrEmpty(Icon?.Dark?.Icon);
|
||||
|
||||
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(Icon?.Icon ?? string.Empty, 10);
|
||||
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(Icon?.Dark?.Icon ?? string.Empty, 10);
|
||||
|
||||
public Windows.UI.Color Foreground
|
||||
{
|
||||
|
||||
@@ -2,21 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks;
|
||||
using Microsoft.CmdPal.Ext.Calc;
|
||||
using Microsoft.CmdPal.Ext.Registry;
|
||||
using Microsoft.CmdPal.Ext.Settings;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices;
|
||||
using Microsoft.CmdPal.Ext.WindowsSettings;
|
||||
using Microsoft.CmdPal.Ext.WindowsTerminal;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using Windows.Foundation;
|
||||
using WindowsCommandPalette.BuiltinCommands;
|
||||
using WindowsCommandPalette.Models;
|
||||
|
||||
namespace WindowsCommandPalette.Views;
|
||||
|
||||
public class CommandAlias(string shortcut, string commandId, bool direct = false)
|
||||
|
||||
@@ -13,7 +13,7 @@ public sealed class DetailsViewModel
|
||||
|
||||
internal string Body { get; init; } = string.Empty;
|
||||
|
||||
internal IconDataType HeroImage { get; init; } = new(string.Empty);
|
||||
internal IconData HeroImage { get; init; } = new(string.Empty);
|
||||
|
||||
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(HeroImage.Icon);
|
||||
|
||||
@@ -21,6 +21,6 @@ public sealed class DetailsViewModel
|
||||
{
|
||||
this.Title = details.Title;
|
||||
this.Body = details.Body;
|
||||
this.HeroImage = details.HeroImage ?? new(string.Empty);
|
||||
this.HeroImage = details.HeroImage?.Dark ?? new(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using Microsoft.CmdPal.Ext.Bookmarks;
|
||||
using Microsoft.CmdPal.Ext.Calc;
|
||||
using Microsoft.CmdPal.Ext.Indexer;
|
||||
using Microsoft.CmdPal.Ext.Registry;
|
||||
using Microsoft.CmdPal.Ext.Settings;
|
||||
using Microsoft.CmdPal.Ext.Shell;
|
||||
using Microsoft.CmdPal.Ext.WebSearch;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices;
|
||||
@@ -59,7 +58,6 @@ public sealed class MainViewModel : IDisposable
|
||||
BuiltInCommands.Add(new IndexerCommandsProvider());
|
||||
BuiltInCommands.Add(new BookmarksCommandProvider());
|
||||
BuiltInCommands.Add(new CalculatorCommandProvider());
|
||||
BuiltInCommands.Add(new SettingsCommandProvider());
|
||||
BuiltInCommands.Add(_quitCommandProvider);
|
||||
BuiltInCommands.Add(_reloadCommandProvider);
|
||||
BuiltInCommands.Add(new WindowsTerminalCommandsProvider());
|
||||
|
||||
@@ -65,14 +65,23 @@ namespace Microsoft.CmdPal.Extensions
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass IconDataType {
|
||||
IconDataType(String iconString);
|
||||
static IconDataType FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
runtimeclass IconData {
|
||||
IconData(String iconString);
|
||||
static IconData FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
|
||||
String Icon { get; };
|
||||
Windows.Storage.Streams.IRandomAccessStreamReference Data { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass IconInfo {
|
||||
IconInfo(String iconString);
|
||||
IconInfo(IconData lightIcon, IconData darkIcon);
|
||||
|
||||
IconData Light { get; };
|
||||
IconData Dark { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass KeyChord
|
||||
{
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
author: Mike Griese
|
||||
created on: 2024-07-19
|
||||
last updated: 2024-12-31
|
||||
last updated: 2025-01-08
|
||||
issue id: n/a
|
||||
---
|
||||
|
||||
# Run v2 Extensions SDK
|
||||
|
||||
_aka "DevPal", "PT Run v2", "DevSearch", "Windows Command Palette", this thing has many names. I'll use DevPal throughout the doc_
|
||||
_aka "DevPal", "PT Run v2", "DevSearch", "Windows Command Palette", this thing has many names. I'll use "DevPal" throughout the doc_
|
||||
|
||||
> [NOTE!]
|
||||
> Are you here to just see what the SDK looks like? Skip to the [Actions
|
||||
@@ -59,7 +59,7 @@ functionality.
|
||||
- [Form Pages](#form-pages)
|
||||
- [Other types](#other-types)
|
||||
- [`ContextItem`s](#contextitems)
|
||||
- [`IconDataType`](#icondatatype)
|
||||
- [Icons - `IconInfo` and `IconData`](#icons---iconinfo-and-icondatatype)
|
||||
- [`OptionalColor`](#optionalcolor)
|
||||
- [`Details`](#details)
|
||||
- [`INotifyPropChanged`](#inotifypropchanged)
|
||||
@@ -533,7 +533,7 @@ Use `cs` for samples.
|
||||
interface ICommand requires INotifyPropChanged{
|
||||
String Name{ get; };
|
||||
String Id{ get; };
|
||||
IconDataType Icon{ get; };
|
||||
IconInfo Icon{ get; };
|
||||
}
|
||||
|
||||
enum CommandResultKind {
|
||||
@@ -716,7 +716,7 @@ interface IContextItem {}
|
||||
interface ICommandItem requires INotifyPropChanged {
|
||||
ICommand Command{ get; };
|
||||
IContextItem[] MoreCommands{ get; };
|
||||
IconDataType Icon{ get; };
|
||||
IconInfo Icon{ get; };
|
||||
String Title{ get; };
|
||||
String Subtitle{ get; };
|
||||
}
|
||||
@@ -1045,7 +1045,7 @@ interface ISeparatorFilterItem requires IFilterItem {}
|
||||
interface IFilter requires IFilterItem {
|
||||
String Id { get; };
|
||||
String Name { get; };
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
}
|
||||
|
||||
interface IFilters {
|
||||
@@ -1223,10 +1223,10 @@ flyout. Mostly, these are just commands and seperators.
|
||||
If an `ICommandContextItem` has `MoreCommands`, then when it's invoked, we'll
|
||||
create a sub-menu with those items in it.
|
||||
|
||||
#### `IconDataType`
|
||||
#### Icons - `IconInfo` and `IconData`
|
||||
|
||||
This is a wrapper type for passing information about an icon to DevPal. This
|
||||
allows extensions to specify apps in a variety of ways, including:
|
||||
`IconData` is a wrapper type for passing information about an icon to
|
||||
DevPal. This allows extensions to specify apps in a variety of ways, including:
|
||||
|
||||
* A URL to an image on the web or filesystem
|
||||
* A string for an emoji or Segoe Fluent icon
|
||||
@@ -1235,15 +1235,25 @@ allows extensions to specify apps in a variety of ways, including:
|
||||
extensions that want to pass us raw image data, which isn't necessarily a file
|
||||
which DevPal can load itself.
|
||||
|
||||
When specifying icons, elements can specify both the light theme and dark theme
|
||||
versions of an icon with `IconInfo`.
|
||||
|
||||
<!-- In .CS because it's manually added to the idl -->
|
||||
```cs
|
||||
struct IconDataType {
|
||||
IconDataType(String iconString);
|
||||
static IconDataType FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
struct IconData {
|
||||
IconData(String iconString);
|
||||
static IconData FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
|
||||
String Icon { get; };
|
||||
Windows.Storage.Streams.IRandomAccessStreamReference Data { get; };
|
||||
}
|
||||
struct IconInfo {
|
||||
IconInfo(String iconString);
|
||||
IconInfo(IconData lightIcon, IconData darkIcon);
|
||||
|
||||
IconData Light { get; };
|
||||
IconData Dark { get; };
|
||||
}
|
||||
```
|
||||
|
||||
Terminal already has a robust arbitrary string -> icon loader that we can easily
|
||||
@@ -1302,7 +1312,7 @@ block, and the generator will pull this into the file first. -->
|
||||
|
||||
```c#
|
||||
interface ITag {
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
String Text { get; };
|
||||
OptionalColor Foreground { get; };
|
||||
OptionalColor Background { get; };
|
||||
@@ -1317,7 +1327,7 @@ interface IDetailsElement {
|
||||
IDetailsData Data { get; };
|
||||
}
|
||||
interface IDetails {
|
||||
IconDataType HeroImage { get; };
|
||||
IconInfo HeroImage { get; };
|
||||
String Title { get; };
|
||||
String Body { get; };
|
||||
IDetailsElement[] Metadata { get; };
|
||||
@@ -1383,7 +1393,7 @@ interface ICommandProvider requires Windows.Foundation.IClosable, INotifyItemsCh
|
||||
{
|
||||
String Id { get; };
|
||||
String DisplayName { get; };
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
ICommandSettings Settings { get; };
|
||||
Boolean Frozen { get; };
|
||||
|
||||
@@ -1585,9 +1595,12 @@ For example, we should have something like:
|
||||
|
||||
```cs
|
||||
class OpenUrlAction(string targetUrl, ActionResult result) : Microsoft.Windows.Run.Extensions.InvokableCommand {
|
||||
public string Name => "Open";
|
||||
public IconDataType Icon => "\uE8A7"; // OpenInNewWindow
|
||||
public ActionResult Invoke() {
|
||||
public OpenUrlAction()
|
||||
{
|
||||
Name = "Open";
|
||||
Icon = new("\uE8A7"); // OpenInNewWindow
|
||||
}
|
||||
public CommandResult Invoke() {
|
||||
Process.Start(new ProcessStartInfo(targetUrl) { UseShellExecute = true });
|
||||
return result;
|
||||
}
|
||||
@@ -1599,12 +1612,17 @@ that no longer do we need to add additional classes for the actions. We just use
|
||||
the helper:
|
||||
|
||||
```cs
|
||||
class NewsListItem(NewsPost post) : Microsoft.Windows.Run.Extensions.ListItem {
|
||||
public string Title => post.Title;
|
||||
public string Subtitle => post.Url;
|
||||
class NewsListItem : Microsoft.Windows.Run.Extensions.ListItem {
|
||||
private NewsPost _post;
|
||||
public NewsListItem(NewsPost post)
|
||||
{
|
||||
_post = post;
|
||||
Title = post.Title;
|
||||
Subtitle = post.Url;
|
||||
}
|
||||
public IContextItem[] Commands => [
|
||||
new CommandContextItem(new Microsoft.Windows.Run.Extensions.OpenUrlAction(post.Url, ActionResult.KeepOpen)),
|
||||
new CommandContextItem(new Microsoft.Windows.Run.Extensions.OpenUrlAction(post.CommentsUrl, ActionResult.KeepOpen){
|
||||
new CommandContextItem(new OpenUrlAction(post.Url, CommandResult.KeepOpen)),
|
||||
new CommandContextItem(new OpenUrlAction(post.CommentsUrl, CommandResult.KeepOpen){
|
||||
Name = "Open comments",
|
||||
Icon = "\uE8F2" // ChatBubbles
|
||||
})
|
||||
@@ -1612,7 +1630,10 @@ class NewsListItem(NewsPost post) : Microsoft.Windows.Run.Extensions.ListItem {
|
||||
public ITag[] Tags => [ new Tag(){ Text=post.Poster, new Tag(){ Text=post.Points } } ];
|
||||
}
|
||||
class HackerNewsPage: Microsoft.Windows.Run.Extensions.ListPage {
|
||||
public bool Loading => true;
|
||||
public HackerNewsPage()
|
||||
{
|
||||
Loading = true;
|
||||
}
|
||||
IListItem[] GetItems(String query) {
|
||||
List<NewsItem> items = /* do some RSS feed stuff */;
|
||||
this.IsLoading = false;
|
||||
@@ -1832,7 +1853,7 @@ When displaying a page:
|
||||
|
||||
## Class diagram
|
||||
|
||||
This is a diagram attempting to show the relationships between the various types we've defined for the SDK. Some elements are omitted for clarity. (Notably, `IconDataType` and `IPropChanged`, which are used in many places.)
|
||||
This is a diagram attempting to show the relationships between the various types we've defined for the SDK. Some elements are omitted for clarity. (Notably, `IconData` and `IPropChanged`, which are used in many places.)
|
||||
|
||||
The notes on the arrows help indicate the multiplicity of the relationship.
|
||||
* "*" means 0 or more (for arrays)
|
||||
@@ -1844,7 +1865,7 @@ classDiagram
|
||||
class ICommand {
|
||||
String Name
|
||||
String Id
|
||||
IconDataType Icon
|
||||
IconInfo Icon
|
||||
}
|
||||
IPage --|> ICommand
|
||||
class IPage {
|
||||
@@ -1889,7 +1910,7 @@ classDiagram
|
||||
class IFilter {
|
||||
String Id
|
||||
String Name
|
||||
IconDataType Icon
|
||||
IconInfo Icon
|
||||
}
|
||||
|
||||
class IFilters {
|
||||
@@ -1905,7 +1926,7 @@ classDiagram
|
||||
|
||||
%% IListItem --|> INotifyPropChanged
|
||||
class IListItem {
|
||||
IconDataType Icon
|
||||
IconInfo Icon
|
||||
String Title
|
||||
String Subtitle
|
||||
ICommand Command
|
||||
@@ -1948,14 +1969,14 @@ classDiagram
|
||||
}
|
||||
|
||||
class IDetails {
|
||||
IconDataType HeroImage
|
||||
IconInfo HeroImage
|
||||
String Title
|
||||
String Body
|
||||
IDetailsElement[] Metadata
|
||||
}
|
||||
|
||||
class ITag {
|
||||
IconDataType Icon
|
||||
IconInfo Icon
|
||||
String Text
|
||||
Color Color
|
||||
String ToolTip
|
||||
@@ -1980,7 +2001,7 @@ classDiagram
|
||||
|
||||
class ICommandProvider {
|
||||
String DisplayName
|
||||
IconDataType Icon
|
||||
IconInfo Icon
|
||||
Boolean Frozen
|
||||
|
||||
ICommandItem[] TopLevelCommands()
|
||||
|
||||
@@ -8,7 +8,7 @@ public class Command : BaseObservable, ICommand
|
||||
{
|
||||
private string _name = string.Empty;
|
||||
private string _id = string.Empty;
|
||||
private IconDataType _icon = new(string.Empty);
|
||||
private IconInfo _icon = new(string.Empty);
|
||||
|
||||
public string Name
|
||||
{
|
||||
@@ -22,7 +22,7 @@ public class Command : BaseObservable, ICommand
|
||||
|
||||
public string Id { get => _id; protected set => _id = value; }
|
||||
|
||||
public IconDataType Icon
|
||||
public IconInfo Icon
|
||||
{
|
||||
get => _icon;
|
||||
set
|
||||
|
||||
@@ -6,13 +6,13 @@ namespace Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
public partial class CommandItem : BaseObservable, ICommandItem
|
||||
{
|
||||
private IconDataType? _icon;
|
||||
private IconInfo? _icon;
|
||||
private string _title = string.Empty;
|
||||
private string _subtitle = string.Empty;
|
||||
private ICommand? _command;
|
||||
private IContextItem[] _moreCommands = [];
|
||||
|
||||
public IconDataType? Icon
|
||||
public IconInfo? Icon
|
||||
{
|
||||
get => _icon ?? _command?.Icon;
|
||||
set
|
||||
|
||||
@@ -12,7 +12,7 @@ public abstract partial class CommandProvider : ICommandProvider
|
||||
|
||||
private string _displayName = string.Empty;
|
||||
|
||||
private IconDataType _icon = new(string.Empty);
|
||||
private IconInfo _icon = new(string.Empty);
|
||||
|
||||
private ICommandSettings? _settings;
|
||||
|
||||
@@ -20,7 +20,7 @@ public abstract partial class CommandProvider : ICommandProvider
|
||||
|
||||
public string DisplayName { get => _displayName; protected set => _displayName = value; }
|
||||
|
||||
public IconDataType Icon { get => _icon; protected set => _icon = value; }
|
||||
public IconInfo Icon { get => _icon; protected set => _icon = value; }
|
||||
|
||||
public event TypedEventHandler<object, ItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ namespace Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
public class Details : BaseObservable, IDetails
|
||||
{
|
||||
private IconDataType _heroImage = new(string.Empty);
|
||||
private IconInfo _heroImage = new(string.Empty);
|
||||
private string _title = string.Empty;
|
||||
private string _body = string.Empty;
|
||||
private IDetailsElement[] _metadata = [];
|
||||
|
||||
public IconDataType HeroImage
|
||||
public IconInfo HeroImage
|
||||
{
|
||||
get => _heroImage;
|
||||
set
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
public class Filter : IFilter
|
||||
{
|
||||
public IconDataType Icon => throw new NotImplementedException();
|
||||
public IconInfo Icon => throw new NotImplementedException();
|
||||
|
||||
public string Id => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ public class Tag : BaseObservable, ITag
|
||||
{
|
||||
private OptionalColor _foreground;
|
||||
private OptionalColor _background;
|
||||
private IconDataType _icon = new(string.Empty);
|
||||
private IconInfo _icon = new(string.Empty);
|
||||
private string _text = string.Empty;
|
||||
private string _toolTip = string.Empty;
|
||||
private ICommand? _command;
|
||||
@@ -33,7 +33,7 @@ public class Tag : BaseObservable, ITag
|
||||
}
|
||||
}
|
||||
|
||||
public IconDataType Icon
|
||||
public IconInfo Icon
|
||||
{
|
||||
get => _icon;
|
||||
set
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "pch.h"
|
||||
#include "IconData.h"
|
||||
#include "IconData.g.cpp"
|
||||
#include "IconInfo.g.cpp"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "IconData.g.h"
|
||||
#include "IconInfo.g.h"
|
||||
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::implementation
|
||||
{
|
||||
struct IconData : IconDataT<IconData>
|
||||
{
|
||||
IconData(hstring iconPath) :
|
||||
Icon(iconPath){};
|
||||
|
||||
IconData(Windows::Storage::Streams::IRandomAccessStreamReference iconData) :
|
||||
Data(iconData){};
|
||||
|
||||
static Microsoft::CmdPal::Extensions::IconData FromStream(Windows::Storage::Streams::IRandomAccessStreamReference stream)
|
||||
{
|
||||
return *winrt::make_self<IconData>(stream);
|
||||
}
|
||||
|
||||
til::property<hstring> Icon;
|
||||
til::property<Windows::Storage::Streams::IRandomAccessStreamReference> Data;
|
||||
};
|
||||
}
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::factory_implementation
|
||||
{
|
||||
struct IconData : IconDataT<IconData, implementation::IconData>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::implementation
|
||||
{
|
||||
struct IconInfo : IconInfoT<IconInfo>
|
||||
{
|
||||
IconInfo(hstring iconPath) :
|
||||
Light(iconPath),
|
||||
Dark(iconPath){};
|
||||
|
||||
IconInfo(Extensions::IconData light, Extensions::IconData dark) :
|
||||
Light(light),
|
||||
Dark(dark) {};
|
||||
|
||||
til::property<Extensions::IconData> Light;
|
||||
til::property<Extensions::IconData> Dark;
|
||||
};
|
||||
}
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::factory_implementation
|
||||
{
|
||||
struct IconInfo : IconInfoT<IconInfo, implementation::IconInfo>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "IconDataType.h"
|
||||
#include "IconDataType.g.cpp"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
#include "IconDataType.g.h"
|
||||
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::implementation
|
||||
{
|
||||
struct IconDataType : IconDataTypeT<IconDataType>
|
||||
{
|
||||
IconDataType(hstring iconPath) :
|
||||
Icon(iconPath){};
|
||||
|
||||
IconDataType(Windows::Storage::Streams::IRandomAccessStreamReference iconData) :
|
||||
Data(iconData){};
|
||||
|
||||
static Microsoft::CmdPal::Extensions::IconDataType FromStream(Windows::Storage::Streams::IRandomAccessStreamReference stream)
|
||||
{
|
||||
return *winrt::make_self<IconDataType>(stream);
|
||||
}
|
||||
|
||||
til::property<hstring> Icon;
|
||||
til::property<Windows::Storage::Streams::IRandomAccessStreamReference> Data;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
namespace winrt::Microsoft::CmdPal::Extensions::factory_implementation
|
||||
{
|
||||
struct IconDataType : IconDataTypeT<IconDataType, implementation::IconDataType>
|
||||
{
|
||||
};
|
||||
}
|
||||
@@ -15,14 +15,23 @@ namespace Microsoft.CmdPal.Extensions
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass IconDataType {
|
||||
IconDataType(String iconString);
|
||||
static IconDataType FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
runtimeclass IconData {
|
||||
IconData(String iconString);
|
||||
static IconData FromStream(Windows.Storage.Streams.IRandomAccessStreamReference stream);
|
||||
|
||||
String Icon { get; };
|
||||
Windows.Storage.Streams.IRandomAccessStreamReference Data { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass IconInfo {
|
||||
IconInfo(String iconString);
|
||||
IconInfo(IconData lightIcon, IconData darkIcon);
|
||||
|
||||
IconData Light { get; };
|
||||
IconData Dark { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
runtimeclass KeyChord
|
||||
{
|
||||
@@ -62,7 +71,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
interface ICommand requires INotifyPropChanged{
|
||||
String Name{ get; };
|
||||
String Id{ get; };
|
||||
IconDataType Icon{ get; };
|
||||
IconInfo Icon{ get; };
|
||||
}
|
||||
|
||||
enum CommandResultKind {
|
||||
@@ -114,7 +123,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
interface IFilter requires IFilterItem {
|
||||
String Id { get; };
|
||||
String Name { get; };
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
@@ -139,7 +148,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
interface ITag {
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
String Text { get; };
|
||||
OptionalColor Foreground { get; };
|
||||
OptionalColor Background { get; };
|
||||
@@ -157,7 +166,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
}
|
||||
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetails {
|
||||
IconDataType HeroImage { get; };
|
||||
IconInfo HeroImage { get; };
|
||||
String Title { get; };
|
||||
String Body { get; };
|
||||
IDetailsElement[] Metadata { get; };
|
||||
@@ -234,7 +243,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
interface ICommandItem requires INotifyPropChanged {
|
||||
ICommand Command{ get; };
|
||||
IContextItem[] MoreCommands{ get; };
|
||||
IconDataType Icon{ get; };
|
||||
IconInfo Icon{ get; };
|
||||
String Title{ get; };
|
||||
String Subtitle{ get; };
|
||||
}
|
||||
@@ -323,7 +332,7 @@ namespace Microsoft.CmdPal.Extensions
|
||||
{
|
||||
String Id { get; };
|
||||
String DisplayName { get; };
|
||||
IconDataType Icon { get; };
|
||||
IconInfo Icon { get; };
|
||||
ICommandSettings Settings { get; };
|
||||
Boolean Frozen { get; };
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="IconDataType.h" />
|
||||
<ClInclude Include="IconData.h" />
|
||||
<ClInclude Include="PropChangedEventArgs.h" />
|
||||
<ClInclude Include="ItemsChangedEventArgs.h" />
|
||||
<ClInclude Include="KeyChord.h" />
|
||||
@@ -151,7 +151,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IconDataType.cpp" />
|
||||
<ClCompile Include="IconData.cpp" />
|
||||
<ClCompile Include="PropChangedEventArgs.cpp" />
|
||||
<ClCompile Include="ItemsChangedEventArgs.cpp" />
|
||||
<ClCompile Include="KeyChord.cpp" />
|
||||
|
||||
Reference in New Issue
Block a user