Refactor FileTreeViewItem to actually be reactive

This commit is contained in:
trawzified 2024-06-09 10:17:16 +02:00
parent 741276acf3
commit 2556c54e99
2 changed files with 55 additions and 46 deletions

View File

@ -2188,12 +2188,12 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="{x:Type TreeViewItem}"> <Style TargetType="{x:Type TreeViewItem}" x:Key="TreeViewItem">
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="MinHeight" Value="21" /> <Setter Property="MinHeight" Value="21" />
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" /> <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" /> <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property="Padding" Value="2,4,2,3" /> <Setter Property="Padding" Value="4,6,4,6" />
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" /> <Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}" /> <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}" />
<Setter Property="Template"> <Setter Property="Template">
@ -2275,14 +2275,7 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</Style> </Style>
<Style TargetType="{x:Type wj:FileTreeViewItemVM}"> <Style TargetType="{x:Type wj:FileTreeViewItem}" BasedOn="{StaticResource TreeViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="MinHeight" Value="21" />
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property="Padding" Value="4,6,4,6" />
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}" />
<Setter Property="Template"> <Setter Property="Template">
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}"> <ControlTemplate TargetType="{x:Type TreeViewItem}">
@ -2312,7 +2305,7 @@
<ColumnDefinition Width="5*" /> <ColumnDefinition Width="5*" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ic:SymbolIcon IsFilled="True" Symbol="{Binding Path=Symbol, RelativeSource={RelativeSource TemplatedParent}}" Margin="0, 0, 4, 0" Foreground="{StaticResource PrimaryBrush}"/> <ic:SymbolIcon IsFilled="True" Symbol="{Binding Path=Header.Symbol, RelativeSource={RelativeSource TemplatedParent}}" Margin="0, 0, 4, 0" Foreground="{StaticResource PrimaryBrush}"/>
<StackPanel Orientation="Horizontal" Grid.Column="1"> <StackPanel Orientation="Horizontal" Grid.Column="1">
<ContentPresenter <ContentPresenter
x:Name="PART_Header" x:Name="PART_Header"
@ -2320,11 +2313,11 @@
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
ContentSource="Header"/> ContentSource="Header"/>
</StackPanel> </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"/> <ComboBox Name="CompilerFileStateListbox" ItemsSource="{wj:EnumToItemsSource {x:Type wj:CompilerFileState}}" DisplayMemberPath="DisplayName" SelectedValue="{Binding Path=Header.CompilerFileState, RelativeSource={RelativeSource TemplatedParent}}" SelectedValuePath="Value" Grid.Column="2"/>
<Grid.Style> <Grid.Style>
<Style TargetType="Grid"> <Style TargetType="Grid">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding Path=SpecialFileState, RelativeSource={RelativeSource TemplatedParent}}" Value="True"> <DataTrigger Binding="{Binding Path=Header.SpecialFileState, RelativeSource={RelativeSource TemplatedParent}}" Value="True">
<Setter Property="Background" Value="{StaticResource PrimaryVariantBrush}" /> <Setter Property="Background" Value="{StaticResource PrimaryVariantBrush}" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>

View File

@ -49,35 +49,40 @@ namespace Wabbajack
[Description("Always Enabled")] [Description("Always Enabled")]
AlwaysEnabled AlwaysEnabled
} }
public class FileTreeViewItemVM : TreeViewItem public class FileTreeViewItem : TreeViewItem
{ {
private CompilerFileState _compilerFileState = CompilerFileState.AutoMatch; public FileTreeViewItem(DirectoryInfo dir)
{
Header = new FileTreeItemVM(dir);
}
public FileTreeViewItem(FileInfo file)
{
Header = new FileTreeItemVM(file);
}
}
public class FileTreeItemVM : ReactiveObject, IDisposable
{
private readonly CompositeDisposable _disposable = new();
public FileSystemInfo Info { get; set; } public FileSystemInfo Info { get; set; }
public bool IsDirectory { get; set; } public bool IsDirectory { get; set; }
public Symbol Symbol { get; set; } public Symbol Symbol { get; set; }
public CompilerFileState CompilerFileState [Reactive] public CompilerFileState CompilerFileState { get; set; }
{
get => _compilerFileState;
set
{
_compilerFileState = value;
SpecialFileState = _compilerFileState != CompilerFileState.AutoMatch;
}
}
public RelativePath PathRelativeToRoot { get; set; } public RelativePath PathRelativeToRoot { get; set; }
[Reactive] public bool SpecialFileState { get; set; } [Reactive] public bool SpecialFileState { get; set; }
public FileTreeViewItemVM(DirectoryInfo info) public FileTreeItemVM(DirectoryInfo info)
{ {
Info = info; Info = info;
IsDirectory = true; IsDirectory = true;
Header = info.Name;
Symbol = Symbol.Folder; Symbol = Symbol.Folder;
this.WhenAnyValue(ftvivm => ftvivm.CompilerFileState)
.Subscribe(cfs => SpecialFileState = cfs != CompilerFileState.AutoMatch)
.DisposeWith(_disposable);
} }
public FileTreeViewItemVM(FileInfo info) public FileTreeItemVM(FileInfo info)
{ {
Info = info; Info = info;
Header = info.Name;
Symbol = info.Extension.ToLower() switch { Symbol = info.Extension.ToLower() switch {
".7z" or ".zip" or ".rar" or ".bsa" or ".ba2" or ".wabbajack" or ".tar" or ".tar.gz" => Symbol.Archive, ".7z" or ".zip" or ".rar" or ".bsa" or ".ba2" or ".wabbajack" or ".tar" or ".tar.gz" => Symbol.Archive,
".toml" or ".ini" or ".cfg" or ".json" or ".yaml" or ".xml" or ".yml" or ".meta" => Symbol.DocumentSettings, ".toml" or ".ini" or ".cfg" or ".json" or ".yaml" or ".xml" or ".yml" or ".meta" => Symbol.DocumentSettings,
@ -95,8 +100,17 @@ namespace Wabbajack
_ => Symbol.Document _ => Symbol.Document
}; };
SpecialFileState = CompilerFileState != CompilerFileState.AutoMatch; SpecialFileState = CompilerFileState != CompilerFileState.AutoMatch;
this.WhenAnyValue(ftvivm => ftvivm.CompilerFileState)
.Subscribe(cfs => SpecialFileState = cfs != CompilerFileState.AutoMatch)
.DisposeWith(_disposable);
}
public override string ToString() => Info.Name;
public void Dispose()
{
GC.SuppressFinalize(this);
_disposable.Dispose();
} }
public override string ToString() => Info.FullName;
} }
public class CompilerFileManagerVM : BackNavigatingVM public class CompilerFileManagerVM : BackNavigatingVM
{ {
@ -149,7 +163,7 @@ namespace Wabbajack
private IEnumerable<TreeViewItem> LoadFiles(DirectoryInfo parent) private IEnumerable<TreeViewItem> LoadFiles(DirectoryInfo parent)
{ {
var parentTreeItem = new FileTreeViewItemVM(parent) var parentTreeItem = new FileTreeViewItem(parent)
{ {
IsExpanded = true, IsExpanded = true,
ItemsSource = LoadDirectoryContents(parent), ItemsSource = LoadDirectoryContents(parent),
@ -162,36 +176,38 @@ namespace Wabbajack
{ {
return parent.EnumerateDirectories() return parent.EnumerateDirectories()
.OrderBy(dir => dir.Name) .OrderBy(dir => dir.Name)
.Select(dir => new FileTreeViewItemVM(dir) { ItemsSource = (dir.EnumerateDirectories().Any() || dir.EnumerateFiles().Any()) ? new ObservableCollection<TreeViewItem>([new TreeViewItem() { Header = "Loading..." }]) : null}).Select(item => { .Select(dir => new FileTreeViewItem(dir) { ItemsSource = (dir.EnumerateDirectories().Any() || dir.EnumerateFiles().Any()) ? new ObservableCollection<TreeViewItem>([new TreeViewItem() { Header = "Loading..." }]) : null}).Select(item => {
item.Expanded += LoadingItem_Expanded; item.Expanded += LoadingItem_Expanded;
item.PathRelativeToRoot = ((AbsolutePath)item.Info.FullName).RelativeTo(Settings.Source); var header = (FileTreeItemVM)item.Header;
if (Settings.NoMatchInclude.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.NoMatchInclude; } header.PathRelativeToRoot = ((AbsolutePath)header.Info.FullName).RelativeTo(Settings.Source);
else if(Settings.Include.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.Include; } if (Settings.NoMatchInclude.Contains(header.PathRelativeToRoot)) { header.CompilerFileState = CompilerFileState.NoMatchInclude; }
else if(Settings.Ignore.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.Ignore; } else if(Settings.Include.Contains(header.PathRelativeToRoot)) { header.CompilerFileState = CompilerFileState.Include; }
else if(Settings.AlwaysEnabled.Contains(item.PathRelativeToRoot)) { item.CompilerFileState = CompilerFileState.AlwaysEnabled; } else if(Settings.Ignore.Contains(header.PathRelativeToRoot)) { header.CompilerFileState = CompilerFileState.Ignore; }
item.SpecialFileState = item.CompilerFileState != CompilerFileState.AutoMatch; else if(Settings.AlwaysEnabled.Contains(header.PathRelativeToRoot)) { header.CompilerFileState = CompilerFileState.AlwaysEnabled; }
while(!item.SpecialFileState) header.SpecialFileState = header.CompilerFileState != CompilerFileState.AutoMatch;
while(!header.SpecialFileState)
{ {
item.SpecialFileState = Settings.NoMatchInclude.Any(p => item.PathRelativeToRoot.InFolder(p)); header.SpecialFileState = Settings.NoMatchInclude.Any(p => header.PathRelativeToRoot.InFolder(p));
item.SpecialFileState = Settings.Include.Any(p => item.PathRelativeToRoot.InFolder(p)); header.SpecialFileState = Settings.Include.Any(p => header.PathRelativeToRoot.InFolder(p));
item.SpecialFileState = Settings.Ignore.Any(p => item.PathRelativeToRoot.InFolder(p)); header.SpecialFileState = Settings.Ignore.Any(p => header.PathRelativeToRoot.InFolder(p));
item.SpecialFileState = Settings.AlwaysEnabled.Any(p => item.PathRelativeToRoot.InFolder(p)); header.SpecialFileState = Settings.AlwaysEnabled.Any(p => header.PathRelativeToRoot.InFolder(p));
break; break;
} }
item.Header = header;
return item; return item;
}) })
.Concat(parent.EnumerateFiles() .Concat(parent.EnumerateFiles()
.OrderBy(file => file.Name) .OrderBy(file => file.Name)
.Select(file => new FileTreeViewItemVM(file))); .Select(file => new FileTreeViewItem(file)));
} }
private void LoadingItem_Expanded(object sender, System.Windows.RoutedEventArgs e) private void LoadingItem_Expanded(object sender, System.Windows.RoutedEventArgs e)
{ {
var parent = (FileTreeViewItemVM)e.OriginalSource; var parent = (FileTreeViewItem)e.OriginalSource;
var children = parent.ItemsSource.OfType<TreeViewItem>(); var children = parent.ItemsSource.OfType<TreeViewItem>();
var firstChild = children.Any() ? children.First().Header : null; var firstChild = children.Any() ? children.First().Header : null;
if (firstChild != null && firstChild is string firstString && firstString == "Loading...") if (firstChild != null && firstChild is string firstString && firstString == "Loading...")
parent.ItemsSource = LoadDirectoryContents((DirectoryInfo)parent.Info); parent.ItemsSource = LoadDirectoryContents((DirectoryInfo)((FileTreeItemVM)parent.Header).Info);
} }
private IEnumerable<FileSystemInfo> GetDirectoryContents(DirectoryInfo dir) private IEnumerable<FileSystemInfo> GetDirectoryContents(DirectoryInfo dir)