Browse Source

Merge pull request #43 from ionite34/launch-options-search

pull/5/head
Ionite 1 year ago committed by GitHub
parent
commit
8fad27796d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/workflows/build.yml
  2. 78
      StabilityMatrix/Helper/Cache/LRUCache.cs
  3. 2
      StabilityMatrix/Helper/DialogFactory.cs
  4. 55
      StabilityMatrix/LaunchOptionsDialog.xaml
  5. 6
      StabilityMatrix/Models/LaunchOptionCard.cs
  6. 15
      StabilityMatrix/SettingsPage.xaml
  7. 1
      StabilityMatrix/StabilityMatrix.csproj
  8. 52
      StabilityMatrix/ViewModels/LaunchOptionsDialogViewModel.cs
  9. 11
      StabilityMatrix/ViewModels/SettingsViewModel.cs

1
.github/workflows/build.yml

@ -23,6 +23,7 @@ jobs:
dotnet-version: '6.0.x'
- name: Cache NuGet packages
id: cache
uses: actions/cache@v1
with:
path: ~/.nuget/packages

78
StabilityMatrix/Helper/Cache/LRUCache.cs

@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace StabilityMatrix.Helper.Cache;
// ReSharper disable once InconsistentNaming
public class LRUCache<TK,TV> where TK : notnull
{
private readonly int capacity;
private readonly Dictionary<TK, LinkedListNode<LRUCacheItem<TK, TV>>> cacheMap = new();
private readonly LinkedList<LRUCacheItem<TK, TV>> lruList = new();
public LRUCache(int capacity)
{
this.capacity = capacity;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public TV? Get(TK key)
{
if (cacheMap.TryGetValue(key, out var node))
{
var value = node.Value.Value;
lruList.Remove(node);
lruList.AddLast(node);
return value;
}
return default;
}
public bool Get(TK key, out TV? value)
{
value = Get(key);
return value != null;
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void Add(TK key, TV val)
{
if (cacheMap.TryGetValue(key, out var existingNode))
{
lruList.Remove(existingNode);
}
else if (cacheMap.Count >= capacity)
{
RemoveFirst();
}
var cacheItem = new LRUCacheItem<TK, TV>(key, val);
var node = new LinkedListNode<LRUCacheItem<TK, TV>>(cacheItem);
lruList.AddLast(node);
cacheMap[key] = node;
}
private void RemoveFirst()
{
// Remove from LRUPriority
var node = lruList.First;
lruList.RemoveFirst();
if (node == null) return;
// Remove from cache
cacheMap.Remove(node.Value.Key);
}
}
// ReSharper disable once InconsistentNaming
internal class LRUCacheItem<TK,TV>
{
public LRUCacheItem(TK k, TV v)
{
Key = k;
Value = v;
}
public TK Key;
public TV Value;
}

2
StabilityMatrix/Helper/DialogFactory.cs

@ -23,7 +23,7 @@ public class DialogFactory : IDialogFactory
public LaunchOptionsDialog CreateLaunchOptionsDialog(IEnumerable<LaunchOptionDefinition> definitions, InstalledPackage installedPackage)
{
launchOptionsDialogViewModel.Cards.Clear();
launchOptionsDialogViewModel.Clear();
// Create cards
launchOptionsDialogViewModel.CardsFromDefinitions(definitions);
// Load user settings

55
StabilityMatrix/LaunchOptionsDialog.xaml

@ -1,13 +1,12 @@
<ui:ContentDialog
CloseButtonText="Close"
DialogHeight="512"
DialogWidth="640"
DialogHeight="616"
DialogWidth="760"
Loaded="LaunchOptionsDialog_OnLoaded"
Title="Launch Options"
d:DataContext="{d:DesignInstance Type=viewModels:LaunchOptionsDialogViewModel,
IsDesignTimeCreatable=True}"
d:DesignHeight="512"
d:DesignWidth="512"
d:DesignHeight="616"
d:DesignWidth="760"
mc:Ignorable="d"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
@ -114,7 +113,8 @@
HorizontalAlignment="Stretch"
Margin="8"
PlaceholderText="{Binding DefaultValue, Mode=OneWay, Converter={StaticResource LaunchOptionConverter}}"
ValidationMode="InvalidInputOverwritten"
SpinButtonPlacementMode="Compact"
ValidationMode="Disabled"
Value="{Binding OptionValue, Converter={StaticResource LaunchOptionIntDoubleConverter}, Mode=TwoWay}"
VerticalAlignment="Stretch" />
</StackPanel>
@ -144,6 +144,45 @@
</DataTemplate>
</ui:ContentDialog.Resources>
<!-- Options cards -->
<ItemsControl ItemTemplate="{StaticResource LaunchOptionCardDataTemplate}" ItemsSource="{Binding Cards}" />
<ui:ContentDialog.Title>
<StackPanel
HorizontalAlignment="Stretch"
Margin="8,0,8,0"
Orientation="Vertical">
<!-- Title -->
<TextBlock
FontSize="24"
FontWeight="Bold"
Margin="8,0,8,8"
Text="{Binding Title}"
TextWrapping="Wrap" />
<!-- Search box -->
<ui:TextBox
HorizontalAlignment="Stretch"
IconPlacement="Right"
Margin="0,8,0,0"
MaxWidth="300"
PlaceholderEnabled="True"
PlaceholderText="Search..."
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Top"
Visibility="{Binding IsSearchBoxEnabled, Converter={StaticResource BooleanToVisibilityConverter}}"
x:Name="SearchBox">
<ui:TextBox.Icon>
<ui:SymbolIcon Symbol="Search28" />
</ui:TextBox.Icon>
</ui:TextBox>
</StackPanel>
</ui:ContentDialog.Title>
<!-- Option Cards -->
<ItemsControl
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
ItemTemplate="{StaticResource LaunchOptionCardDataTemplate}"
ItemsSource="{Binding FilteredCards}"
Margin="16,0,0,0"
MaxWidth="400"
MinWidth="300" />
</ui:ContentDialog>

6
StabilityMatrix/Models/LaunchOptionCard.cs

@ -8,6 +8,12 @@ public class LaunchOptionCard
public LaunchOptionType Type { get; set; }
public string? Description { get; set; }
public ObservableCollection<LaunchOption> Options { get; set; } = new();
public LaunchOptionCard(string title, LaunchOptionType type = LaunchOptionType.Bool)
{
Title = title;
Type = type;
}
public LaunchOptionCard(LaunchOptionDefinition definition)
{

15
StabilityMatrix/SettingsPage.xaml

@ -90,12 +90,15 @@
FontSize="16"
FontWeight="Bold"
Margin="0,8"
Text="Some Other Setting" />
<ComboBox
ItemsSource="{Binding AvailableThemes}"
Margin="8"
SelectedItem="{Binding SelectedTheme, Mode=TwoWay}"
Width="500" />
Text="Directories" />
<ui:Button
Command="{Binding OpenAppDataDirectoryCommand}"
Content="App Data"
Margin="8">
<ui:Button.Icon>
<ui:SymbolIcon Margin="4" Symbol="Open32" />
</ui:Button.Icon>
</ui:Button>
</StackPanel>
</ui:Card>
</StackPanel>

1
StabilityMatrix/StabilityMatrix.csproj

@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageReference Include="FuzzySharp" Version="2.0.2" />
<PackageReference Include="Markdown.Xaml" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />

52
StabilityMatrix/ViewModels/LaunchOptionsDialogViewModel.cs

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Helper.Cache;
using StabilityMatrix.Models;
namespace StabilityMatrix.ViewModels;
@ -12,6 +14,47 @@ public partial class LaunchOptionsDialogViewModel : ObservableObject
{
public ObservableCollection<LaunchOptionCard> Cards { get; set; } = new();
[ObservableProperty]
private string title = "Launch Options";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FilteredCards))]
private string searchText = string.Empty;
[ObservableProperty]
private bool isSearchBoxEnabled = true;
private LRUCache<string, ImmutableList<LaunchOptionCard>> cache = new(100);
/// <summary>
/// Return cards that match the search text
/// </summary>
public IEnumerable<LaunchOptionCard> FilteredCards
{
get
{
var text = SearchText;
if (string.IsNullOrWhiteSpace(text) || text.Length < 2)
{
return Cards;
}
// Try cache
if (cache.Get(text, out var cachedCards))
{
return cachedCards!;
}
var searchCard = new LaunchOptionCard(text.ToLowerInvariant());
var extracted = FuzzySharp.Process
.ExtractTop(searchCard, Cards, c => c.Title.ToLowerInvariant());
var results = extracted
.Where(r => r.Score > 40)
.Select(r => r.Value)
.ToImmutableList();
cache.Add(text, results);
return results;
}
}
/// <summary>
/// Export the current cards options to a list of strings
/// </summary>
@ -53,6 +96,15 @@ public partial class LaunchOptionsDialogViewModel : ObservableObject
}
}
/// <summary>
/// Clear Cards and cache
/// </summary>
public void Clear()
{
cache = new LRUCache<string, ImmutableList<LaunchOptionCard>>(100);
Cards.Clear();
}
public void OnLoad()
{
Debug.WriteLine("In LaunchOptions OnLoad");

11
StabilityMatrix/ViewModels/SettingsViewModel.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
@ -121,6 +122,16 @@ public partial class SettingsViewModel : ObservableObject
await dialog.ShowAsync();
}
}
[RelayCommand]
private void OpenAppDataDirectory()
{
// Open app data in file explorer
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var appPath = Path.Combine(appDataPath, "StabilityMatrix");
Process.Start("explorer.exe", appPath);
}
public async Task OnLoaded()
{

Loading…
Cancel
Save