Merge pull request #37 from zadjii-msft/dev/crutkas/moreBaseCleanup

Dev/crutkas/more base cleanup
This commit is contained in:
Clint Rutkas
2024-09-04 20:59:10 -07:00
committed by GitHub
33 changed files with 769 additions and 692 deletions

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Threading.Tasks;
using AllApps.Programs;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
@@ -19,6 +18,7 @@ internal sealed class AppAction : InvokableCommand
this._app = app;
this._Icon = new(_app.IcoPath);
}
internal static async Task StartApp(string amuid)
{
var appManager = new ApplicationActivationManager();
@@ -34,15 +34,18 @@ internal sealed class AppAction : InvokableCommand
}
}).ConfigureAwait(false);
}
internal static async Task StartExe(string path)
{
var appManager = new ApplicationActivationManager();
// const ActivateOptions noFlags = ActivateOptions.None;
await Task.Run(() =>
{
Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
});
}
internal async Task Launch()
{
if (string.IsNullOrEmpty(_app.ExePath))
@@ -54,6 +57,7 @@ internal sealed class AppAction : InvokableCommand
await StartExe(_app.ExePath);
}
}
public override ActionResult Invoke()
{
_ = Launch();

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
namespace WindowsCommandPalette.BuiltinCommands.AllApps;
@@ -11,7 +10,8 @@ namespace WindowsCommandPalette.BuiltinCommands.AllApps;
// NOTE this is pretty close to what we'd put in the SDK
internal sealed class OpenPathAction(string target) : InvokableCommand
{
private readonly string _Target = target;
private readonly string _target = target;
internal static async Task LaunchTarget(string t)
{
await Task.Run(() =>
@@ -19,9 +19,11 @@ internal sealed class OpenPathAction(string target) : InvokableCommand
Process.Start(new ProcessStartInfo(t) { UseShellExecute = true });
});
}
public override ActionResult Invoke()
{
LaunchTarget(this._Target).Start();
LaunchTarget(_target).Start();
return ActionResult.GoHome();
}
}

View File

@@ -2,7 +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;
using System.Runtime.InteropServices;
namespace AllApps.Programs;
@@ -12,4 +11,4 @@ namespace AllApps.Programs;
[ComImport]
public class AppxFactory
{
}
}

View File

@@ -2,7 +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;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -44,4 +43,4 @@ public class ApplicationActivationManager : IApplicationActivationManager
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
}
}

View File

@@ -17,4 +17,4 @@ public interface IAppxFactory
void _VtblGap0_2(); // skip 2 methods
internal IAppxManifestReader CreateManifestReader(IStream inputStream);
}
}

View File

@@ -2,9 +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;
using System.Runtime.InteropServices;
// using Wox.Plugin.Common.Win32;
namespace AllApps.Programs;
@@ -17,4 +15,4 @@ public interface IAppxManifestApplication
[PreserveSig]
int GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value);
}
}

View File

@@ -17,4 +17,4 @@ namespace AllApps.Programs
bool MoveNext();
}
}
}

View File

@@ -2,9 +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;
using System.Runtime.InteropServices;
// using Wox.Plugin.Common.Win32;
namespace AllApps.Programs
{
@@ -18,4 +16,4 @@ namespace AllApps.Programs
[PreserveSig]
int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
}
}
}

View File

@@ -24,4 +24,4 @@ public interface IAppxManifestReader
void _VtblGap1_5(); // skip 5 methods
IAppxManifestApplicationsEnumerator GetApplications();
}
}

View File

@@ -2,20 +2,19 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace AllApps.Programs
namespace AllApps.Programs;
public interface IPackage
{
public interface IPackage
{
string Name { get; }
string Name { get; }
string FullName { get; }
string FullName { get; }
string FamilyName { get; }
string FamilyName { get; }
bool IsFramework { get; }
bool IsFramework { get; }
bool IsDevelopmentMode { get; }
bool IsDevelopmentMode { get; }
string InstalledLocation { get; }
}
}
string InstalledLocation { get; }
}

View File

@@ -2,12 +2,9 @@
// 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.Generic;
namespace AllApps.Programs;
namespace AllApps.Programs
public interface IPackageManager
{
public interface IPackageManager
{
IEnumerable<IPackage> FindPackagesForCurrentUser();
}
}
IEnumerable<IPackage> FindPackagesForCurrentUser();
}

View File

@@ -0,0 +1,37 @@
// 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.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
namespace WindowsCommandPalette.BuiltinCommands;
public class QuitAction : InvokableCommand, IFallbackHandler
{
public event TypedEventHandler<object?, object?>? QuitRequested;
public QuitAction()
{
Icon = new("\uE711");
}
public override ICommandResult Invoke()
{
QuitRequested?.Invoke(this, new());
return ActionResult.KeepOpen();
}
public void UpdateQuery(string query)
{
if (query.StartsWith('q'))
{
Name = "Quit";
}
else
{
Name = string.Empty;
}
}
}

View File

@@ -8,34 +8,6 @@ using Windows.Foundation;
namespace WindowsCommandPalette.BuiltinCommands;
public class QuitAction : InvokableCommand, IFallbackHandler
{
public event TypedEventHandler<object?, object?>? QuitRequested;
public QuitAction()
{
Icon = new("\uE711");
}
public override ICommandResult Invoke()
{
QuitRequested?.Invoke(this, new());
return ActionResult.KeepOpen();
}
public void UpdateQuery(string query)
{
if (query.StartsWith('q'))
{
Name = "Quit";
}
else
{
Name = string.Empty;
}
}
}
public class QuitActionProvider : ICommandProvider
{
public string DisplayName => string.Empty;

View File

@@ -0,0 +1,108 @@
// 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 System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using CmdPal.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using WindowsCommandPalette.Views;
namespace DeveloperCommandPalette;
// The FilteredListSection is for when we've got any filter at all. It starts by
// enumerating all actions and apps, and returns the subset that matches.
public sealed class FilteredListSection : ISection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public string Title => string.Empty;
private readonly MainViewModel _mainViewModel;
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Top-level list items, from builtin commands and extensions
public ObservableCollection<MainListItem> TopLevelItems { get; set; }
// Apps, from the apps built in command.
private IEnumerable<IListItem> AllApps => _mainViewModel.apps.GetItems().First().Items;
// Results from the last searched text
private IEnumerable<IListItem>? lastSearchResults;
// Things we should enumerate over, depending on the search query.
// This is either
// * the last search results (if there was a query),
// * OR one of:
// * Just the top-level actions (if there's no query)
// * OR the top-level actions AND the apps (if there's a query)
private IEnumerable<IListItem> ItemsToEnumerate =>
lastSearchResults != null ?
lastSearchResults :
TopLevelItems.Concat(AllApps);
private string _lastQuery = string.Empty;
// Setting this will enumerate all the actions and installed apps.
internal string Query
{
get => _lastQuery;
set
{
if (string.IsNullOrEmpty(value) ||
!_lastQuery.StartsWith(value, true, System.Globalization.CultureInfo.CurrentCulture))
{
lastSearchResults = null;
}
if (lastSearchResults != null && string.IsNullOrEmpty(value))
{
// Even with an empty query, we did filter the items (to remove fallbacks without a name
// Here, we're going from an empty query to one that has an actual string. Reset the last, so we will _also_ include apps now.
this.lastSearchResults = null;
}
_lastQuery = value;
var results = ListHelpers.FilterList(ItemsToEnumerate, Query);
this.lastSearchResults = string.IsNullOrEmpty(value) ? null : results;
}
}
// Watch out future me!
//
// Don't do the whole linq query in Items itself. That'll evaluate the whole
// query once per item, because the ListPage.xaml.cs will create one
// ListViewItems per item in Items, and every time it does that, it calls
// section.Items.
//
// instead run the query once when the action query changes, and store the
// results.
public IListItem[] Items => ItemsToEnumerate.Where(i => i != null).ToArray();
public FilteredListSection(MainViewModel viewModel)
{
this._mainViewModel = viewModel;
// TODO: We should probably just get rid of MainListItem entirely, so I'm leaveing these uncaught
TopLevelItems = new(_mainViewModel.TopLevelCommands.Where(wrapper => wrapper.Unsafe != null).Select(wrapper => new MainListItem(wrapper.Unsafe!)));
TopLevelItems.CollectionChanged += Bubble_CollectionChanged;
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
_dispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
internal void Reset()
{
TopLevelItems.Clear();
lastSearchResults = null;
}
}

View File

@@ -48,6 +48,7 @@ public sealed class MainListItem : ListItem
{
this.MoreCommands = this._listItem.MoreCommands;
}
OnPropertyChanged(args.PropertyName);
}
}

View File

@@ -2,240 +2,21 @@
// 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 System.Collections.Specialized;
using System.Linq;
using CmdPal.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using WindowsCommandPalette.Views;
namespace DeveloperCommandPalette;
public sealed class RecentsListSection : ListSection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
private readonly DispatcherQueue DispatcherQueue = DispatcherQueue.GetForCurrentThread();
private readonly MainViewModel _mainViewModel;
internal ObservableCollection<MainListItem> _Items { get; set; } = [];
private bool loadedApps;
public RecentsListSection(MainViewModel viewModel)
{
this.Title = "Recent";
this._mainViewModel = viewModel;
var recent = _mainViewModel.RecentActions;
Reset();
_Items.CollectionChanged += Bubble_CollectionChanged;
_mainViewModel.AppsReady += _mainViewModel_AppsReady;
}
private void _mainViewModel_AppsReady(object sender, object? args)
{
loadedApps = true;
_mainViewModel.AppsReady -= _mainViewModel_AppsReady;
AddApps();
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
DispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
public override IListItem[] Items => _Items.ToArray();
internal void Reset()
{
_Items.Clear();
if (loadedApps)
{
AddApps();
}
}
internal void AddApps()
{
var apps = _mainViewModel.Recent;
foreach (var app in apps)
{
_Items.Add(new MainListItem(app.Unsafe)); // we know these are all local
}
}
}
// The MainListSection is for all non-recent actions. No apps.
public sealed class MainListSection : ISection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public string Title => "Actions";
private readonly MainViewModel _mainViewModel;
private readonly DispatcherQueue DispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Top-level list items, from builtin commands and extensions
internal ObservableCollection<MainListItem> _Items { get; set; }
// Things we should enumerate over, depending on the search query.
// This is either
// * the last search results (if there was a query),
// * OR one of:
// * Just the top-level actions (if there's no query)
// * OR the top-level actions AND the apps (if there's a query)
private IEnumerable<IListItem> itemsToEnumerate =>
_Items.Where(i => i != null && (!_mainViewModel.IsRecentCommand(i)));
// Watch out future me!
//
// Don't do the whole linq query in Items itself. That'll evaluate the whole
// query once per item, because the ListPage.xaml.cs will create one
// ListViewItems per item in Items, and every time it does that, it calls
// section.Items.
//
// instead run the query once when the action query changes, and store the
// results.
public IListItem[] Items => itemsToEnumerate.ToArray();
public MainListSection(MainViewModel viewModel)
{
this._mainViewModel = viewModel;
_Items = new(_mainViewModel.TopLevelCommands.Select(w => w.Unsafe).Where(li => li != null).Select(li => new MainListItem(li!)));
_Items.CollectionChanged += Bubble_CollectionChanged;
}
internal void UpdateQuery(string query)
{
var fallbacks = _Items.Select(i => i?.FallbackHandler).Where(fb => fb != null).Select(fb => fb!);
foreach (var fb in fallbacks)
{
fb.UpdateQuery(query);
}
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
DispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
internal void Reset()
{
_Items.Clear();
}
}
// The FilteredListSection is for when we've got any filter at all. It starts by
// enumerating all actions and apps, and returns the subset that matches.
public sealed class FilteredListSection : ISection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public string Title => string.Empty;
private readonly MainViewModel _mainViewModel;
private readonly DispatcherQueue DispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Top-level list items, from builtin commands and extensions
internal ObservableCollection<MainListItem> _Items { get; set; }
// Apps, from the apps built in command.
private IEnumerable<IListItem> _apps => _mainViewModel.apps.GetItems().First().Items;
// Results from the last searched text
private IEnumerable<IListItem>? lastSearchResults;
// Things we should enumerate over, depending on the search query.
// This is either
// * the last search results (if there was a query),
// * OR one of:
// * Just the top-level actions (if there's no query)
// * OR the top-level actions AND the apps (if there's a query)
private IEnumerable<IListItem> itemsToEnumerate =>
lastSearchResults != null ?
lastSearchResults :
_Items.Concat(_apps);
internal string lastQuery = string.Empty;
// Setting this will enumerate all the actions and installed apps.
internal string Query
{
get => lastQuery;
set
{
if (string.IsNullOrEmpty(value) ||
!lastQuery.StartsWith(value, true, System.Globalization.CultureInfo.CurrentCulture))
{
lastSearchResults = null;
}
if (lastSearchResults != null && string.IsNullOrEmpty(value))
{
// Even with an empty query, we did filter the items (to remove fallbacks without a name
// Here, we're going from an empty query to one that has an actual string. Reset the last, so we will _also_ include apps now.
this.lastSearchResults = null;
}
lastQuery = value;
var results = ListHelpers.FilterList(itemsToEnumerate, Query);
this.lastSearchResults = string.IsNullOrEmpty(value) ? null : results;
}
}
// Watch out future me!
//
// Don't do the whole linq query in Items itself. That'll evaluate the whole
// query once per item, because the ListPage.xaml.cs will create one
// ListViewItems per item in Items, and every time it does that, it calls
// section.Items.
//
// instead run the query once when the action query changes, and store the
// results.
public IListItem[] Items => itemsToEnumerate.Where(i => i != null).ToArray();
public FilteredListSection(MainViewModel viewModel)
{
this._mainViewModel = viewModel;
// TODO: We should probably just get rid of MainListItem entirely, so I'm leaveing these uncaught
_Items = new(_mainViewModel.TopLevelCommands.Where(wrapper => wrapper.Unsafe != null).Select(wrapper => new MainListItem(wrapper.Unsafe!)));
_Items.CollectionChanged += Bubble_CollectionChanged;
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
DispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
internal void Reset()
{
_Items.Clear();
lastSearchResults = null;
}
}
public sealed class MainListPage : Microsoft.Windows.CommandPalette.Extensions.Helpers.DynamicListPage
public sealed class MainListPage : DynamicListPage
{
private readonly MainViewModel _mainViewModel;
private readonly MainListSection _mainSection;
private readonly RecentsListSection _recentsListSection;
private readonly FilteredListSection _filteredSection;
private readonly ISection[] _Sections;
private readonly ISection[] _sections;
public MainListPage(MainViewModel viewModel)
{
@@ -247,7 +28,7 @@ public sealed class MainListPage : Microsoft.Windows.CommandPalette.Extensions.H
_mainViewModel.TopLevelCommands.CollectionChanged += TopLevelCommands_CollectionChanged;
_Sections = [
_sections = [
_recentsListSection,
_mainSection
];
@@ -259,7 +40,7 @@ public sealed class MainListPage : Microsoft.Windows.CommandPalette.Extensions.H
public override ISection[] GetItems()
{
return _Sections;
return _sections;
}
public override ISection[] GetItems(string query)
@@ -268,7 +49,7 @@ public sealed class MainListPage : Microsoft.Windows.CommandPalette.Extensions.H
_mainSection.UpdateQuery(query);
if (string.IsNullOrEmpty(query))
{
return _Sections;
return _sections;
}
else
{
@@ -290,7 +71,7 @@ public sealed class MainListPage : Microsoft.Windows.CommandPalette.Extensions.H
_mainSection._Items.Add(new MainListItem(listItem.Unsafe));
}
_filteredSection._Items.Add(new MainListItem(listItem.Unsafe));
_filteredSection.TopLevelItems.Add(new MainListItem(listItem.Unsafe));
}
}
}

View File

@@ -0,0 +1,78 @@
// 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 System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using CmdPal.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using WindowsCommandPalette.Views;
namespace DeveloperCommandPalette;
// The MainListSection is for all non-recent actions. No apps.
public sealed class MainListSection : ISection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public string Title => "Actions";
private readonly MainViewModel _mainViewModel;
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Top-level list items, from builtin commands and extensions
internal ObservableCollection<MainListItem> _Items { get; set; }
// Things we should enumerate over, depending on the search query.
// This is either
// * the last search results (if there was a query),
// * OR one of:
// * Just the top-level actions (if there's no query)
// * OR the top-level actions AND the apps (if there's a query)
private IEnumerable<IListItem> itemsToEnumerate =>
_Items.Where(i => i != null && (!_mainViewModel.IsRecentCommand(i)));
// Watch out future me!
//
// Don't do the whole linq query in Items itself. That'll evaluate the whole
// query once per item, because the ListPage.xaml.cs will create one
// ListViewItems per item in Items, and every time it does that, it calls
// section.Items.
//
// instead run the query once when the action query changes, and store the
// results.
public IListItem[] Items => itemsToEnumerate.ToArray();
public MainListSection(MainViewModel viewModel)
{
this._mainViewModel = viewModel;
_Items = new(_mainViewModel.TopLevelCommands.Select(w => w.Unsafe).Where(li => li != null).Select(li => new MainListItem(li!)));
_Items.CollectionChanged += Bubble_CollectionChanged;
}
internal void UpdateQuery(string query)
{
var fallbacks = _Items.Select(i => i?.FallbackHandler).Where(fb => fb != null).Select(fb => fb!);
foreach (var fb in fallbacks)
{
fb.UpdateQuery(query);
}
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
_dispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
internal void Reset()
{
_Items.Clear();
}
}

View File

@@ -107,9 +107,6 @@
<Copy SourceFiles="$(PkgAdaptiveCards_ObjectModel_WinUI3)\runtimes\win10-$(Platform)\native\AdaptiveCards.ObjectModel.WinUI3.dll" DestinationFolder="$(TargetDir)" />
<Copy SourceFiles="$(PkgAdaptiveCards_Rendering_WinUI3)\runtimes\win10-$(Platform)\native\AdaptiveCards.Rendering.WinUI3.dll" DestinationFolder="$(TargetDir)" />
</Target>
<ItemGroup>
<PRIResource Remove="Builtins\NewFolder\**" />
</ItemGroup>
<!-- </AdaptiveCardsWorkaround> -->
</Project>

View File

@@ -1,18 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Runtime.InteropServices;
using Microsoft.CmdPal.Common.Services;
using Microsoft.Windows.CommandPalette.Extensions;
using Windows.ApplicationModel;
using Windows.ApplicationModel.AppExtensions;
using Windows.Win32;
using Windows.Win32.System.Com;
using WinRT;
// 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.
namespace CmdPal.Models;
public class ExtensionObject<T> // where T : IInspectable
public class ExtensionObject<T> // where T : IInspectable
{
private readonly T _value;

View File

@@ -22,7 +22,7 @@ public class ExtensionWrapper : IExtensionWrapper
private readonly Dictionary<Type, ProviderType> _providerTypeMap = new()
{
[typeof(ICommandProvider)] = ProviderType.Commands
[typeof(ICommandProvider)] = ProviderType.Commands,
};
private IExtension? _extensionObject;

View File

@@ -0,0 +1,74 @@
// 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 System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using CmdPal.Models;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using WindowsCommandPalette.Views;
namespace DeveloperCommandPalette;
public sealed class RecentsListSection : ListSection, INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler? CollectionChanged;
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private readonly MainViewModel _mainViewModel;
internal ObservableCollection<MainListItem> _Items { get; set; } = [];
private bool loadedApps;
public RecentsListSection(MainViewModel viewModel)
{
this.Title = "Recent";
this._mainViewModel = viewModel;
var recent = _mainViewModel.RecentActions;
Reset();
_Items.CollectionChanged += Bubble_CollectionChanged;
_mainViewModel.AppsReady += _mainViewModel_AppsReady;
}
private void _mainViewModel_AppsReady(object sender, object? args)
{
loadedApps = true;
_mainViewModel.AppsReady -= _mainViewModel_AppsReady;
AddApps();
}
private void Bubble_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
_dispatcherQueue.TryEnqueue(() =>
{
CollectionChanged?.Invoke(this, e);
});
}
public override IListItem[] Items => _Items.ToArray();
internal void Reset()
{
_Items.Clear();
if (loadedApps)
{
AddApps();
}
}
internal void AddApps()
{
var apps = _mainViewModel.Recent;
foreach (var app in apps)
{
_Items.Add(new MainListItem(app.Unsafe)); // we know these are all local
}
}
}

View File

@@ -16,8 +16,6 @@ namespace Microsoft.Windows.CommandPalette.Services;
public class ExtensionService : IExtensionService, IDisposable
{
// private readonly ILogger _log = Log.ForContext("SourceContext", nameof(ExtensionService));
public event EventHandler OnExtensionsChanged = (_, _) => { };
private static readonly PackageCatalog _catalog = PackageCatalog.OpenForCurrentUser();

View File

@@ -2,25 +2,10 @@
// 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;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.Terminal.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.CommandPalette.Extensions;
using Windows.ApplicationModel.AppExtensions;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI;
namespace DeveloperCommandPalette;
@@ -46,10 +31,10 @@ public sealed class TagViewModel : INotifyPropertyChanged
internal Brush BackgroundBrush => new SolidColorBrush(Tag.Color.A == 0 ? Tag.Color : Color.FromArgb((byte)(Tag.Color.A / 4), Tag.Color.R, Tag.Color.G, Tag.Color.B));
public TagViewModel(ITag tag)
{
this.Tag = tag;
// this.Tag.PropChanged += Tag_PropertyChanged;
}

View File

@@ -3,30 +3,11 @@
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.CommandPalette.Extensions;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace WindowsCommandPalette.Views;
public sealed class DetailsViewModel
{
internal string Title { get; init; } = string.Empty;
internal string Body { get; init; } = string.Empty;
internal IconDataType HeroImage { get; init; } = new(string.Empty);
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(HeroImage.Icon);
internal DetailsViewModel(IDetails details)
{
this.Title = details.Title;
this.Body = details.Body;
this.HeroImage = details.HeroImage ?? new(string.Empty);
}
}
public sealed partial class DetailsControl : UserControl
{
private readonly DetailsViewModel ViewModel;

View File

@@ -0,0 +1,28 @@
// 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.UI.Xaml.Controls;
using Microsoft.Windows.CommandPalette.Extensions;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace WindowsCommandPalette.Views;
public sealed class DetailsViewModel
{
internal string Title { get; init; } = string.Empty;
internal string Body { get; init; } = string.Empty;
internal IconDataType HeroImage { get; init; } = new(string.Empty);
internal IconElement IcoElement => Microsoft.Terminal.UI.IconPathConverter.IconMUX(HeroImage.Icon);
internal DetailsViewModel(IDetails details)
{
this.Title = details.Title;
this.Body = details.Body;
this.HeroImage = details.HeroImage ?? new(string.Empty);
}
}

View File

@@ -2,162 +2,31 @@
// 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;
using System.Collections.ObjectModel;
using System.ComponentModel;
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Rendering.WinUI3;
using AdaptiveCards.Templating;
using DeveloperCommandPalette;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
using Windows.System;
using Windows.UI.ViewManagement;
namespace WindowsCommandPalette.Views;
public sealed class FormViewModel : System.ComponentModel.INotifyPropertyChanged
{
internal readonly IForm form;
internal AdaptiveCardParseResult? card;
internal RenderedAdaptiveCard? RenderedAdaptiveCard;
internal string TemplateJson = "{}";
internal string DataJson = "{}";
public event TypedEventHandler<object, SubmitFormArgs>? RequestSubmitForm;
public event PropertyChangedEventHandler? PropertyChanged;
public bool ShouldDisplay => RenderedAdaptiveCard?.FrameworkElement != null;
public FrameworkElement? RenderedChildElement => RenderedAdaptiveCard?.FrameworkElement;
public FormViewModel(IForm form)
{
this.form = form;
}
internal void InitialRender()
{
// var t = new Task<bool>(() => {
this.TemplateJson = this.form.TemplateJson();
try
{
this.DataJson = this.form.DataJson();
}
catch (Exception)
{
this.DataJson = "{}";
}
// return true;
// });
// t.Start();
// await t;
AdaptiveCardTemplate template = new(TemplateJson);
var cardJson = template.Expand(DataJson);
this.card = AdaptiveCard.FromJsonString(cardJson);
}
internal void RenderToXaml(AdaptiveCardRenderer renderer)
{
if (this.card != null)
{
RenderedAdaptiveCard = renderer.RenderAdaptiveCard(card.AdaptiveCard);
RenderedAdaptiveCard.Action += RenderedAdaptiveCard_Action;
var handlers = this.PropertyChanged;
handlers?.Invoke(this, new PropertyChangedEventArgs(nameof(ShouldDisplay)));
handlers?.Invoke(this, new PropertyChangedEventArgs(nameof(RenderedChildElement)));
}
}
private async void RenderedAdaptiveCard_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args)
{
if (args.Action is AdaptiveOpenUrlAction openUrlAction)
{
await Launcher.LaunchUriAsync(openUrlAction.Url);
}
else if (args.Action is AdaptiveSubmitAction submitAction)
{
// Get the data and inputs
var data = submitAction.DataJson.Stringify();
var inputs = args.Inputs.AsJson().Stringify();
_ = data;
_ = inputs;
// Process them as desired
var handlers = RequestSubmitForm;
handlers?.Invoke(this, new() { FormData = inputs, Form = form });
}
}
}
public sealed class FormPageViewModel : PageViewModel
{
internal IFormPage Page => (IFormPage)this.pageAction;
internal ObservableCollection<FormViewModel> Forms = new();
public FormPageViewModel(IFormPage page)
: base(page)
{
}
internal async Task InitialRender()
{
var t = new Task<bool>(() =>
{
try
{
var f = this.Page.Forms();
foreach (var form in f)
{
var formVm = new FormViewModel(form);
formVm.RequestSubmitForm += RequestSubmitFormBubbler;
Forms.Add(formVm);
}
}
catch (Exception)
{
}
foreach (var form in this.Forms)
{
form.InitialRender();
}
return true;
});
t.Start();
await t;
}
private void RequestSubmitFormBubbler(object sender, SubmitFormArgs args)
{
this.SubmitForm(args.FormData, args.Form);
}
}
public sealed partial class FormPage : Page
{
private FormPageViewModel? ViewModel;
private readonly AdaptiveCardRenderer Renderer = new();
private FormPageViewModel? ViewModel;
public FormPage()
{
this.InitializeComponent();
UISettings settings = new UISettings();
// yep this is the way to check if you're in light theme or dark.
// yep it's this dumb
var foreground = settings.GetColorValue(UIColorType.Foreground);
var lightTheme = foreground.R < 128;
Renderer.HostConfig = AdaptiveHostConfig.FromJsonString(lightTheme ? LightHostConfig : DarkHostConfig).HostConfig;
}
private void AddCardElement(FormViewModel form)
@@ -170,9 +39,15 @@ public sealed partial class FormPage : Page
base.OnNavigatedTo(e);
ViewModel = (FormPageViewModel?)e.Parameter;
if (ViewModel == null) return;
ViewModel.InitialRender().ContinueWith((t) => {
DispatcherQueue.TryEnqueue(() => {
if (ViewModel == null)
{
return;
}
ViewModel.InitialRender().ContinueWith((t) =>
{
DispatcherQueue.TryEnqueue(() =>
{
foreach (var form in this.ViewModel.Forms)
{
AddCardElement(form);
@@ -193,7 +68,6 @@ public sealed partial class FormPage : Page
ViewModel?.GoBack();
}
private static readonly string DarkHostConfig = """
{
"spacing": {
@@ -515,5 +389,4 @@ public sealed partial class FormPage : Page
}
}
""";
}

View File

@@ -0,0 +1,68 @@
// 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 System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Rendering.WinUI3;
using AdaptiveCards.Templating;
using DeveloperCommandPalette;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
using Windows.System;
using Windows.UI.ViewManagement;
namespace WindowsCommandPalette.Views;
public sealed class FormPageViewModel : PageViewModel
{
internal IFormPage Page => (IFormPage)this.pageAction;
internal ObservableCollection<FormViewModel> Forms = new();
public FormPageViewModel(IFormPage page)
: base(page)
{
}
internal async Task InitialRender()
{
var t = new Task<bool>(() =>
{
try
{
var f = this.Page.Forms();
foreach (var form in f)
{
var formVm = new FormViewModel(form);
formVm.RequestSubmitForm += RequestSubmitFormBubbler;
Forms.Add(formVm);
}
}
catch (Exception)
{
}
foreach (var form in this.Forms)
{
form.InitialRender();
}
return true;
});
t.Start();
await t;
}
private void RequestSubmitFormBubbler(object sender, SubmitFormArgs args)
{
this.SubmitForm(args.FormData, args.Form);
}
}

View File

@@ -0,0 +1,92 @@
// 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 System.ComponentModel;
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Rendering.WinUI3;
using AdaptiveCards.Templating;
using DeveloperCommandPalette;
using Microsoft.UI.Xaml;
using Microsoft.Windows.CommandPalette.Extensions;
using Windows.Foundation;
using Windows.System;
namespace WindowsCommandPalette.Views;
public sealed class FormViewModel : System.ComponentModel.INotifyPropertyChanged
{
internal readonly IForm form;
internal AdaptiveCardParseResult? card;
internal RenderedAdaptiveCard? RenderedAdaptiveCard;
internal string TemplateJson = "{}";
internal string DataJson = "{}";
public event TypedEventHandler<object, SubmitFormArgs>? RequestSubmitForm;
public event PropertyChangedEventHandler? PropertyChanged;
public bool ShouldDisplay => RenderedAdaptiveCard?.FrameworkElement != null;
public FrameworkElement? RenderedChildElement => RenderedAdaptiveCard?.FrameworkElement;
public FormViewModel(IForm form)
{
this.form = form;
}
internal void InitialRender()
{
// var t = new Task<bool>(() => {
this.TemplateJson = this.form.TemplateJson();
try
{
this.DataJson = this.form.DataJson();
}
catch (Exception)
{
this.DataJson = "{}";
}
// return true;
// });
// t.Start();
// await t;
AdaptiveCardTemplate template = new(TemplateJson);
var cardJson = template.Expand(DataJson);
this.card = AdaptiveCard.FromJsonString(cardJson);
}
internal void RenderToXaml(AdaptiveCardRenderer renderer)
{
if (this.card != null)
{
RenderedAdaptiveCard = renderer.RenderAdaptiveCard(card.AdaptiveCard);
RenderedAdaptiveCard.Action += RenderedAdaptiveCard_Action;
var handlers = this.PropertyChanged;
handlers?.Invoke(this, new PropertyChangedEventArgs(nameof(ShouldDisplay)));
handlers?.Invoke(this, new PropertyChangedEventArgs(nameof(RenderedChildElement)));
}
}
private async void RenderedAdaptiveCard_Action(RenderedAdaptiveCard sender, AdaptiveActionEventArgs args)
{
if (args.Action is AdaptiveOpenUrlAction openUrlAction)
{
await Launcher.LaunchUriAsync(openUrlAction.Url);
}
else if (args.Action is AdaptiveSubmitAction submitAction)
{
// Get the data and inputs
var data = submitAction.DataJson.Stringify();
var inputs = args.Inputs.AsJson().Stringify();
_ = data;
_ = inputs;
// Process them as desired
var handlers = RequestSubmitForm;
handlers?.Invoke(this, new() { FormData = inputs, Form = form });
}
}
}

View File

@@ -3,18 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.InteropServices;
using DeveloperCommandPalette;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
@@ -31,10 +22,11 @@ public sealed class ListPageViewModel : PageViewModel
private IDynamicListPage? dynamicPage => Page as IDynamicListPage;
private readonly DispatcherQueue DispatcherQueue = DispatcherQueue.GetForCurrentThread();
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
internal string Query = string.Empty;
public ListPageViewModel(IListPage page) : base(page)
public ListPageViewModel(IListPage page)
: base(page)
{
}

View File

@@ -2,189 +2,18 @@
// 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 System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using CmdPal.Models;
using DeveloperCommandPalette;
using Microsoft.CmdPal.Common.Extensions;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Ext.Bookmarks;
using Microsoft.CmdPal.Ext.Calc;
using Microsoft.CmdPal.Ext.Settings;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
using Windows.Win32;
using WindowsCommandPalette.BuiltinCommands;
using WindowsCommandPalette.BuiltinCommands.AllApps;
namespace WindowsCommandPalette.Views;
public sealed class MainViewModel
{
internal readonly AllAppsPage apps = new();
internal readonly QuitActionProvider quitActionProvider = new();
internal readonly ReloadExtensionsActionProvider reloadActionProvider = new();
public event TypedEventHandler<object, object?>? QuitRequested { add => quitActionProvider.QuitRequested += value; remove => quitActionProvider.QuitRequested -= value; }
internal readonly ObservableCollection<ActionsProviderWrapper> CommandsProviders = new();
internal readonly ObservableCollection<ExtensionObject<IListItem>> TopLevelCommands = [];
internal readonly List<ICommandProvider> _builtInCommands = [];
internal bool Loaded;
internal bool LoadingExtensions;
internal bool LoadedApps;
public event TypedEventHandler<object, object?>? HideRequested;
public event TypedEventHandler<object, object?>? SummonRequested;
public event TypedEventHandler<object, object?>? AppsReady;
internal MainViewModel()
{
_builtInCommands.Add(new BookmarksActionProvider());
_builtInCommands.Add(new CalculatorActionProvider());
_builtInCommands.Add(new SettingsActionProvider());
_builtInCommands.Add(quitActionProvider);
_builtInCommands.Add(reloadActionProvider);
ResetTopLevel();
// On a background thread, warm up the app cache since we want it more often than not
new Task(() =>
{
var _ = AppCache.Instance.Value;
LoadedApps = true;
AppsReady?.Invoke(this, null);
}).Start();
}
public void ResetTopLevel()
{
TopLevelCommands.Clear();
TopLevelCommands.Add(new(new ListItem(apps)));
}
internal void RequestHide()
{
var handlers = HideRequested;
handlers?.Invoke(this, null);
}
public void Summon()
{
var handlers = SummonRequested;
handlers?.Invoke(this, null);
}
private static string CreateHash(string? title, string? subtitle)
{
return title + subtitle;
}
private string[] _recentCommandHashes = [];
public IEnumerable<IListItem> RecentActions => TopLevelCommands
.Select(i => i.Unsafe)
.Where((i) =>
{
if (i != null)
{
try
{
return _recentCommandHashes.Contains(CreateHash(i.Title, i.Subtitle));
}
catch (COMException)
{
return false;
}
}
return false;
}).Select(i => i!);
public IEnumerable<IListItem> AppItems => LoadedApps ? apps.GetItems().First().Items : [];
public IEnumerable<ExtensionObject<IListItem>> Everything => TopLevelCommands
.Concat(AppItems.Select(i => new ExtensionObject<IListItem>(i)))
.Where(i =>
{
var v = i != null;
return v;
});
public IEnumerable<ExtensionObject<IListItem>> Recent => _recentCommandHashes
.Select(hash =>
Everything
.Where(i =>
{
try
{
var o = i.Unsafe;
return CreateHash(o.Title, o.Subtitle) == hash;
}
catch (COMException)
{
return false;
}
})
.FirstOrDefault())
.Where(i => i != null)
.Select(i => i!);
public bool IsRecentCommand(MainListItem item)
{
try
{
foreach (var wraprer in Recent)
{
if (wraprer.Unsafe == item)
{
return true;
}
}
}
catch (COMException)
{
return false;
}
return false;
}
internal void PushRecentAction(ICommand action)
{
foreach (var wrapped in Everything)
{
try
{
var listItem = wrapped?.Unsafe;
if (listItem != null && listItem.Command == action)
{
// Found it, awesome.
var hash = CreateHash(listItem.Title, listItem.Subtitle);
// Remove the old one and push the new one to the front
var recent = new List<string>([hash]).Concat(_recentCommandHashes.Where(h => h != hash)).Take(5).ToArray();
_recentCommandHashes = recent.ToArray();
return;
}
}
catch (COMException)
{ /* log something */
}
}
}
}
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>

View File

@@ -0,0 +1,186 @@
// 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 System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using CmdPal.Models;
using DeveloperCommandPalette;
using Microsoft.CmdPal.Common.Extensions;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.Ext.Bookmarks;
using Microsoft.CmdPal.Ext.Calc;
using Microsoft.CmdPal.Ext.Settings;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.Windows.CommandPalette.Extensions;
using Microsoft.Windows.CommandPalette.Extensions.Helpers;
using Windows.Foundation;
using Windows.Win32;
using WindowsCommandPalette.BuiltinCommands;
using WindowsCommandPalette.BuiltinCommands.AllApps;
namespace WindowsCommandPalette.Views;
public sealed class MainViewModel
{
internal readonly AllAppsPage apps = new();
internal readonly QuitActionProvider quitActionProvider = new();
internal readonly ReloadExtensionsActionProvider reloadActionProvider = new();
public event TypedEventHandler<object, object?>? QuitRequested { add => quitActionProvider.QuitRequested += value; remove => quitActionProvider.QuitRequested -= value; }
internal readonly ObservableCollection<ActionsProviderWrapper> CommandsProviders = new();
internal readonly ObservableCollection<ExtensionObject<IListItem>> TopLevelCommands = [];
internal readonly List<ICommandProvider> _builtInCommands = [];
internal bool Loaded;
internal bool LoadingExtensions;
internal bool LoadedApps;
public event TypedEventHandler<object, object?>? HideRequested;
public event TypedEventHandler<object, object?>? SummonRequested;
public event TypedEventHandler<object, object?>? AppsReady;
internal MainViewModel()
{
_builtInCommands.Add(new BookmarksActionProvider());
_builtInCommands.Add(new CalculatorActionProvider());
_builtInCommands.Add(new SettingsActionProvider());
_builtInCommands.Add(quitActionProvider);
_builtInCommands.Add(reloadActionProvider);
ResetTopLevel();
// On a background thread, warm up the app cache since we want it more often than not
new Task(() =>
{
var _ = AppCache.Instance.Value;
LoadedApps = true;
AppsReady?.Invoke(this, null);
}).Start();
}
public void ResetTopLevel()
{
TopLevelCommands.Clear();
TopLevelCommands.Add(new(new ListItem(apps)));
}
internal void RequestHide()
{
var handlers = HideRequested;
handlers?.Invoke(this, null);
}
public void Summon()
{
var handlers = SummonRequested;
handlers?.Invoke(this, null);
}
private static string CreateHash(string? title, string? subtitle)
{
return title + subtitle;
}
private string[] _recentCommandHashes = [];
public IEnumerable<IListItem> RecentActions => TopLevelCommands
.Select(i => i.Unsafe)
.Where((i) =>
{
if (i != null)
{
try
{
return _recentCommandHashes.Contains(CreateHash(i.Title, i.Subtitle));
}
catch (COMException)
{
return false;
}
}
return false;
}).Select(i => i!);
public IEnumerable<IListItem> AppItems => LoadedApps ? apps.GetItems().First().Items : [];
public IEnumerable<ExtensionObject<IListItem>> Everything => TopLevelCommands
.Concat(AppItems.Select(i => new ExtensionObject<IListItem>(i)))
.Where(i =>
{
var v = i != null;
return v;
});
public IEnumerable<ExtensionObject<IListItem>> Recent => _recentCommandHashes
.Select(hash =>
Everything
.Where(i =>
{
try
{
var o = i.Unsafe;
return CreateHash(o.Title, o.Subtitle) == hash;
}
catch (COMException)
{
return false;
}
})
.FirstOrDefault())
.Where(i => i != null)
.Select(i => i!);
public bool IsRecentCommand(MainListItem item)
{
try
{
foreach (var wraprer in Recent)
{
if (wraprer.Unsafe == item)
{
return true;
}
}
}
catch (COMException)
{
return false;
}
return false;
}
internal void PushRecentAction(ICommand action)
{
foreach (var wrapped in Everything)
{
try
{
var listItem = wrapped?.Unsafe;
if (listItem != null && listItem.Command == action)
{
// Found it, awesome.
var hash = CreateHash(listItem.Title, listItem.Subtitle);
// Remove the old one and push the new one to the front
var recent = new List<string>([hash]).Concat(_recentCommandHashes.Where(h => h != hash)).Take(5).ToArray();
_recentCommandHashes = recent.ToArray();
return;
}
}
catch (COMException)
{ /* log something */
}
}
}
}

View File

@@ -14,36 +14,7 @@ using Microsoft.Windows.CommandPalette.Extensions;
namespace WindowsCommandPalette.Views;
public sealed class MarkdownPageViewModel : PageViewModel
{
internal IMarkdownPage Page => (IMarkdownPage)this.pageAction;
internal string[] MarkdownContent = [string.Empty];
internal string Title => Page.Title;
private IEnumerable<ICommandContextItem> contextActions => Page.Commands.Where(i => i is ICommandContextItem).Select(i => (ICommandContextItem)i);
internal bool HasMoreCommands => contextActions.Any();
internal IList<ContextItemViewModel> ContextActions => contextActions.Select(a => new ContextItemViewModel(a)).ToList();
public MarkdownPageViewModel(IMarkdownPage page)
: base(page)
{
}
internal async Task InitialRender(MarkdownPage markdownPage)
{
var t = new Task<string[]>(() => {
return this.Page.Bodies();
});
t.Start();
this.MarkdownContent = await t;
}
}
public sealed partial class MarkdownPage : Page, System.ComponentModel.INotifyPropertyChanged
public sealed partial class MarkdownPage : Page, INotifyPropertyChanged
{
private MarkdownPageViewModel? ViewModel;
@@ -66,7 +37,8 @@ public sealed partial class MarkdownPage : Page, System.ComponentModel.INotifyPr
return;
}
ViewModel.InitialRender(this).ContinueWith((t) => {
ViewModel.InitialRender(this).ContinueWith((t) =>
{
DispatcherQueue.TryEnqueue(() =>
{
if (ViewModel.MarkdownContent.Length > 0)

View File

@@ -0,0 +1,37 @@
// 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 DeveloperCommandPalette;
using Microsoft.Windows.CommandPalette.Extensions;
namespace WindowsCommandPalette.Views;
public sealed class MarkdownPageViewModel : PageViewModel
{
internal IMarkdownPage Page => (IMarkdownPage)this.pageAction;
internal string[] MarkdownContent = [string.Empty];
internal string Title => Page.Title;
private IEnumerable<ICommandContextItem> contextActions => Page.Commands.Where(i => i is ICommandContextItem).Select(i => (ICommandContextItem)i);
internal bool HasMoreCommands => contextActions.Any();
internal IList<ContextItemViewModel> ContextActions => contextActions.Select(a => new ContextItemViewModel(a)).ToList();
public MarkdownPageViewModel(IMarkdownPage page)
: base(page)
{
}
internal async Task InitialRender(MarkdownPage markdownPage)
{
var t = new Task<string[]>(() => {
return this.Page.Bodies();
});
t.Start();
this.MarkdownContent = await t;
}
}