Add interactive CompilerFileState setting combo box to compiler file manager

This commit is contained in:
trawzified 2024-06-08 19:02:14 +02:00
parent 2937dce6f1
commit f5c9e075c7
8 changed files with 138 additions and 43 deletions

View File

@ -0,0 +1,49 @@
using SteamKit2.GC.Artifact.Internal;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace Wabbajack
{
public class EnumToItemsSource : MarkupExtension
{
private readonly Type _type;
public EnumToItemsSource(Type type)
{
_type = type;
}
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
if (attributes != null && attributes.Any())
{
return attributes.First().Description;
}
return value.ToString();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Enum.GetValues(_type)
.Cast<Enum>()
.Select(e =>
{
return new
{
Value = e,
DisplayName = GetEnumDescription((Enum)e)
};
});
}
}
}

View File

@ -25,7 +25,7 @@
<local:AbsolutePathToStringConverter x:Key="AbsolutePathToStringConverter" />
<local:FileSizeConverter x:Key="FileSizeConverter"/>
<local:WidthHeightRectConverter x:Key="WidthHeightRectConverter" />
<math:MathConverter x:Key="MathConverter"/>
<math:MathConverter x:Key="MathConverter" />
<!-- Colors -->
<Color x:Key="WindowBackgroundColor">#222531</Color>
@ -2303,16 +2303,25 @@
x:Name="Expander"
ClickMode="Press"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Style="{DynamicResource TreeViewToggleButtonStyle}" />
Style="{DynamicResource TreeViewToggleButtonStyle}"
Foreground="{StaticResource PrimaryBrush}"/>
<StackPanel Orientation="Horizontal" Grid.Column="1">
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ic:SymbolIcon IsFilled="True" Symbol="{Binding Path=Symbol, RelativeSource={RelativeSource TemplatedParent}}" Margin="0, 0, 4, 0" Foreground="{StaticResource PrimaryBrush}"/>
<ContentPresenter
<StackPanel Orientation="Horizontal" Grid.Column="1">
<ContentPresenter
x:Name="PART_Header"
Margin="4, 0, 0, 0"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
ContentSource="Header" />
</StackPanel>
ContentSource="Header"/>
</StackPanel>
<ComboBox Name="CompilerFileStateListbox" ItemsSource="{wj:EnumToItemsSource {x:Type wj:CompilerFileState}}" DisplayMemberPath="DisplayName" SelectedValue="{Binding Path=CompilerFileState, RelativeSource={RelativeSource TemplatedParent}}" SelectedValuePath="Value" Grid.Column="2"/>
</Grid>
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost" />

View File

@ -122,7 +122,7 @@ namespace Wabbajack
.Select(p => !string.IsNullOrWhiteSpace(p)))
.Select(v => v.First && v.Second));
*/
NextCommand = ReactiveCommand.CreateFromTask(async () => await NextPage());
NextCommand = ReactiveCommand.CreateFromTask(NextPage);
ModlistLocation = new FilePickerVM
{
@ -220,10 +220,10 @@ namespace Wabbajack
Settings.OutputFile = newSettings.OutputFile.Combine(newSettings.ModListName).WithExtension(Ext.Wabbajack);
Settings.Game = newSettings.Game;
Settings.Include = newSettings.Include;
Settings.Ignore = newSettings.Ignore;
Settings.AlwaysEnabled = newSettings.AlwaysEnabled;
Settings.NoMatchInclude = newSettings.NoMatchInclude;
Settings.Include = newSettings.Include.ToHashSet();
Settings.Ignore = newSettings.Ignore.ToHashSet();
Settings.AlwaysEnabled = newSettings.AlwaysEnabled.ToHashSet();
Settings.NoMatchInclude = newSettings.NoMatchInclude.ToHashSet();
Settings.AdditionalProfiles = newSettings.AdditionalProfiles;
}
@ -264,6 +264,7 @@ namespace Wabbajack
private async Task NextPage()
{
await SaveSettingsFile();
NavigateToGlobal.Send(ScreenType.CompilerFileManager);
LoadCompilerSettings.Send(Settings.ToCompilerSettings());
}
@ -409,43 +410,43 @@ namespace Wabbajack
public void AddAlwaysEnabled(RelativePath path)
{
Settings.AlwaysEnabled = (Settings.AlwaysEnabled ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
Settings.AlwaysEnabled = (Settings.AlwaysEnabled ?? new()).Append(path).Distinct().ToHashSet();
}
public void RemoveAlwaysEnabled(RelativePath path)
{
Settings.AlwaysEnabled = Settings.AlwaysEnabled.Where(p => p != path).ToArray();
Settings.AlwaysEnabled = Settings.AlwaysEnabled.Where(p => p != path).ToHashSet();
}
public void AddNoMatchInclude(RelativePath path)
{
Settings.NoMatchInclude = (Settings.NoMatchInclude ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
Settings.NoMatchInclude = (Settings.NoMatchInclude ?? new()).Append(path).Distinct().ToHashSet();
}
public void RemoveNoMatchInclude(RelativePath path)
{
Settings.NoMatchInclude = Settings.NoMatchInclude.Where(p => p != path).ToArray();
Settings.NoMatchInclude = Settings.NoMatchInclude.Where(p => p != path).ToHashSet();
}
public void AddInclude(RelativePath path)
{
Settings.Include = (Settings.Include ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
Settings.Include = (Settings.Include ?? new()).Append(path).Distinct().ToHashSet();
}
public void RemoveInclude(RelativePath path)
{
Settings.Include = Settings.Include.Where(p => p != path).ToArray();
Settings.Include = Settings.Include.Where(p => p != path).ToHashSet();
}
public void AddIgnore(RelativePath path)
{
Settings.Ignore = (Settings.Ignore ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
Settings.Ignore = (Settings.Ignore ?? new()).Append(path).Distinct().ToHashSet();
}
public void RemoveIgnore(RelativePath path)
{
Settings.Ignore = Settings.Ignore.Where(p => p != path).ToArray();
Settings.Ignore = Settings.Ignore.Where(p => p != path).ToHashSet();
}
#endregion

View File

@ -31,15 +31,22 @@ using Wabbajack.Services.OSIntegrated;
using NexusMods.Paths.FileTree;
using System.Windows.Controls;
using FluentIcons.Common;
using System.Windows.Input;
using System.ComponentModel;
namespace Wabbajack
{
public enum State
public enum CompilerFileState
{
[Description("Auto Match")]
AutoMatch,
[Description("No Match Include")]
NoMatchInclude,
[Description("Force Include")]
Include,
[Description("Force Ignore")]
Ignore,
[Description("Always Enabled")]
AlwaysEnabled
}
public class FileTreeViewItemVM : TreeViewItem
@ -48,7 +55,7 @@ namespace Wabbajack
public FileSystemInfo Info { get; set; }
public bool IsDirectory { get; set; }
public Symbol Symbol { get; set; }
public State CompilerState { get; set; }
public CompilerFileState CompilerFileState { get; set; }
public RelativePath PathRelativeToRoot { get; set; }
public FileTreeViewItemVM(DirectoryInfo info)
{
@ -92,6 +99,7 @@ namespace Wabbajack
[Reactive] public CompilerSettingsVM Settings { get; set; } = new();
public IEnumerable<TreeViewItem> Files { get; set; }
public ICommand PrevCommand { get; set; }
public CompilerFileManagerVM(ILogger<CompilerFileManagerVM> logger, DTOSerializer dtos, SettingsManager settingsManager,
@ -113,6 +121,7 @@ namespace Wabbajack
})
.DisposeWith(CompositeDisposable);
PrevCommand = ReactiveCommand.Create(PrevPage);
this.WhenActivated(disposables =>
{
var fileTree = GetDirectoryContents(new DirectoryInfo(Settings.Source.ToString()));
@ -121,12 +130,18 @@ namespace Wabbajack
});
}
private void PrevPage()
{
NavigateToGlobal.Send(ScreenType.CompilerDetails);
LoadCompilerSettings.Send(Settings.ToCompilerSettings());
}
private IEnumerable<TreeViewItem> LoadFiles(DirectoryInfo parent)
{
var parentTreeItem = new FileTreeViewItemVM(parent)
{
IsExpanded = true,
ItemsSource = LoadDirectoryContents(parent)
ItemsSource = LoadDirectoryContents(parent),
};
return [parentTreeItem];
@ -139,10 +154,10 @@ namespace Wabbajack
.Select(dir => new FileTreeViewItemVM(dir) { ItemsSource = (dir.EnumerateDirectories().Any() || dir.EnumerateFiles().Any()) ? new ObservableCollection<TreeViewItem>([new TreeViewItem() { Header = "Loading..." }]) : null}).Select(item => {
item.Expanded += LoadingItem_Expanded;
item.PathRelativeToRoot = ((AbsolutePath)item.Info.FullName).RelativeTo(Settings.Source);
if (Settings.NoMatchInclude.Contains(item.PathRelativeToRoot)) { item.CompilerState = State.NoMatchInclude; }
else if(Settings.Include.Contains(item.PathRelativeToRoot)) { item.CompilerState = State.Include; }
else if(Settings.Ignore.Contains(item.PathRelativeToRoot)) { item.CompilerState = State.Ignore; }
else if(Settings.AlwaysEnabled.Contains(item.PathRelativeToRoot)) { item.CompilerState = State.AlwaysEnabled; }
if (Settings.NoMatchInclude.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.NoMatchInclude; }
else if(Settings.Include.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.Include; }
else if(Settings.Ignore.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.Ignore; }
else if(Settings.AlwaysEnabled.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.AlwaysEnabled; }
return item;
})

View File

@ -36,10 +36,10 @@ public class CompilerSettingsVM : ViewModel
MachineUrl = cs.MachineUrl;
Profile = cs.Profile;
AdditionalProfiles = cs.AdditionalProfiles;
NoMatchInclude = cs.NoMatchInclude;
Include = cs.Include;
Ignore = cs.Ignore;
AlwaysEnabled = cs.AlwaysEnabled;
NoMatchInclude = cs.NoMatchInclude.ToHashSet();
Include = cs.Include.ToHashSet();
Ignore = cs.Ignore.ToHashSet();
AlwaysEnabled = cs.AlwaysEnabled.ToHashSet();
Version = cs.Version?.ToString() ?? "";
Description = cs.Description;
}
@ -90,19 +90,19 @@ public class CompilerSettingsVM : ViewModel
/// This file, or files in these folders, are automatically included if they don't match
/// any other step
/// </summary>
[Reactive] public RelativePath[] NoMatchInclude { get; set; } = Array.Empty<RelativePath>();
[Reactive] public HashSet<RelativePath> NoMatchInclude { get; set; } = new();
/// <summary>
/// These files are inlined into the modlist
/// </summary>
[Reactive] public RelativePath[] Include { get; set; } = Array.Empty<RelativePath>();
[Reactive] public HashSet<RelativePath> Include { get; set; } = new();
/// <summary>
/// These files are ignored when compiling the modlist
/// </summary>
[Reactive] public RelativePath[] Ignore { get; set; } = Array.Empty<RelativePath>();
[Reactive] public HashSet<RelativePath> Ignore { get; set; } = new();
[Reactive] public RelativePath[] AlwaysEnabled { get; set; } = Array.Empty<RelativePath>();
[Reactive] public HashSet<RelativePath> AlwaysEnabled { get; set; } = new();
[Reactive] public string Version { get; set; }
[Reactive] public string Description { get; set; }
@ -130,10 +130,10 @@ public class CompilerSettingsVM : ViewModel
MachineUrl = MachineUrl,
Profile = Profile,
AdditionalProfiles = AdditionalProfiles,
NoMatchInclude = NoMatchInclude,
Include = Include,
Ignore = Ignore,
AlwaysEnabled = AlwaysEnabled,
NoMatchInclude = NoMatchInclude.ToArray(),
Include = Include.ToArray(),
Ignore = Ignore.ToArray(),
AlwaysEnabled = AlwaysEnabled.ToArray(),
Version = System.Version.Parse(Version),
Description = Description
};

View File

@ -130,7 +130,7 @@ namespace Wabbajack
var searchTextPredicates = this.ObservableForProperty(vm => vm.Search)
.Throttle(searchThrottle, RxApp.MainThreadScheduler)
.Select(change => change.Value.Trim())
.Select(change => change.Value?.Trim() ?? "")
.StartWith(Search)
.Select<string, Func<GalleryModListMetadataVM, bool>>(txt =>
{
@ -193,8 +193,8 @@ namespace Wabbajack
var searchSorter = this.WhenValueChanged(vm => vm.Search)
.Throttle(searchThrottle, RxApp.MainThreadScheduler)
.Select(s => SortExpressionComparer<GalleryModListMetadataVM>
.Descending(m => m.Metadata.Title.StartsWith(s, StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(m => m.Metadata.Title.Contains(s, StringComparison.InvariantCultureIgnoreCase))
.Descending(m => m.Metadata.Title.StartsWith(s ?? "", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(m => m.Metadata.Title.Contains(s ?? "", StringComparison.InvariantCultureIgnoreCase))
.ThenByDescending(m => !m.IsBroken));
_modLists.Connect()
.ObserveOn(RxApp.MainThreadScheduler)

View File

@ -16,6 +16,24 @@
d:DesignWidth="800"
x:TypeArguments="local:CompilerFileManagerVM"
mc:Ignorable="d">
<TreeView x:Name="FileTreeView">
</TreeView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="7*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TreeView x:Name="FileTreeView"/>
<Grid Grid.Row="1" Margin="0, 16, 0, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button x:Name="PrevButton" Content="Back to Modlist Details" Margin="0, 0, 16, 0"/>
<Button Grid.Column="1" x:Name="ReinferSettingsButton" Content="Re-infer Settings" Margin="0, 0, 16, 0"/>
<Button Grid.Column="5" x:Name="ContinueButton" Content="Continue" Margin="16, 0, 0, 0"/>
</Grid>
</Grid>
</rxui:ReactiveUserControl>

View File

@ -35,6 +35,9 @@ namespace Wabbajack
.BindToStrict(this, v => v.FileTreeView.ItemsSource)
.DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.PrevCommand, v => v.PrevButton)
.DisposeWith(disposables);
});
}