Merge pull request #372 from GICodeWarrior/patch-1

Improve TreeView and Export performance
This commit is contained in:
Valentin 2023-03-18 22:34:49 +01:00 committed by GitHub
commit 928834fd4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 33 deletions

View File

@ -4,6 +4,8 @@
xmlns:local="clr-namespace:FModel" xmlns:local="clr-namespace:FModel"
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
xmlns:settings="clr-namespace:FModel.Settings"
xmlns:services="clr-namespace:FModel.Services"
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI" xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI" xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI" xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
@ -95,7 +97,7 @@
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Header="Auto Open Sounds" IsCheckable="True" StaysOpenOnClick="True" <MenuItem Header="Auto Open Sounds" IsCheckable="True" StaysOpenOnClick="True"
IsChecked="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" /> IsChecked="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" />
</MenuItem> </MenuItem>
<MenuItem Header="Views"> <MenuItem Header="Views">
<MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer"> <MenuItem Header="3D Viewer" Command="{Binding MenuCommand}" CommandParameter="Views_3dViewer">
@ -221,7 +223,7 @@
<TextBlock Grid.Row="0" Grid.Column="0" Text="Loading Mode" VerticalAlignment="Center" /> <TextBlock Grid.Row="0" Grid.Column="0" Text="Loading Mode" VerticalAlignment="Center" />
<ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding LoadingModes.Modes}" IsEnabled="{Binding Status.IsReady}" <ComboBox Grid.Row="0" Grid.Column="2" ItemsSource="{Binding LoadingModes.Modes}" IsEnabled="{Binding Status.IsReady}"
SelectedItem="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}"> SelectedItem="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}, Mode=TwoWay}">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" /> <TextBlock Text="{Binding Converter={x:Static converters:EnumToStringConverter.Instance}}" />
@ -327,7 +329,7 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
<Separator Grid.Row="1" Style="{StaticResource CustomSeparator}" Margin="0" /> <Separator Grid.Row="1" Style="{StaticResource CustomSeparator}" Margin="0" />
<TreeView Grid.Row="2" x:Name="AssetsFolderName" Style="{StaticResource AssetsFolderTreeView}" PreviewMouseDoubleClick="OnAssetsTreeMouseDoubleClick"> <TreeView Grid.Row="2" x:Name="AssetsFolderName" Style="{StaticResource AssetsFolderTreeView}" PreviewMouseDoubleClick="OnAssetsTreeMouseDoubleClick" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" ScrollViewer.CanContentScroll="True">
<TreeView.ContextMenu> <TreeView.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="Extract Folder's Packages" Click="OnFolderExtractClick"> <MenuItem Header="Extract Folder's Packages" Click="OnFolderExtractClick">
@ -671,7 +673,7 @@
<TabControl Grid.Row="0" x:Name="TabControlName" Style="{StaticResource GameFilesTabControl}" /> <TabControl Grid.Row="0" x:Name="TabControlName" Style="{StaticResource GameFilesTabControl}" />
<Expander Grid.Row="1" Margin="0 5 0 5" ExpandDirection="Down" <Expander Grid.Row="1" Margin="0 5 0 5" ExpandDirection="Down"
IsExpanded="{Binding IsLoggerExpanded, Source={x:Static local:Settings.UserSettings.Default}}"> IsExpanded="{Binding IsLoggerExpanded, Source={x:Static settings:UserSettings.Default}}">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@ -734,7 +736,7 @@
<Setter Property="Background" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" /> <Setter Property="Background" Value="{DynamicResource {x:Static adonisUi:Brushes.ErrorBrush}}" />
<Setter Property="Foreground" Value="White" /> <Setter Property="Foreground" Value="White" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding StatusChangeAttempted, Source={x:Static local:Services.ApplicationService.ThreadWorkerView}}" Value="True"> <DataTrigger Binding="{Binding StatusChangeAttempted, Source={x:Static services:ApplicationService.ThreadWorkerView}}" Value="True">
<DataTrigger.EnterActions> <DataTrigger.EnterActions>
<BeginStoryboard> <BeginStoryboard>
<Storyboard Duration="0:0:0.8"> <Storyboard Duration="0:0:0.8">
@ -751,7 +753,7 @@
</BeginStoryboard> </BeginStoryboard>
</DataTrigger.EnterActions> </DataTrigger.EnterActions>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding OperationCancelled, Source={x:Static local:Services.ApplicationService.ThreadWorkerView}}" Value="True"> <DataTrigger Binding="{Binding OperationCancelled, Source={x:Static services:ApplicationService.ThreadWorkerView}}" Value="True">
<DataTrigger.EnterActions> <DataTrigger.EnterActions>
<BeginStoryboard> <BeginStoryboard>
<Storyboard Duration="0:0:1"> <Storyboard Duration="0:0:1">
@ -780,7 +782,7 @@
</DataTrigger> </DataTrigger>
<MultiDataTrigger> <MultiDataTrigger>
<MultiDataTrigger.Conditions> <MultiDataTrigger.Conditions>
<Condition Binding="{Binding CanBeCanceled, Source={x:Static local:Services.ApplicationService.ThreadWorkerView}}" Value="True" /> <Condition Binding="{Binding CanBeCanceled, Source={x:Static services:ApplicationService.ThreadWorkerView}}" Value="True" />
<Condition Binding="{Binding Status.Kind}" Value="{x:Static local:EStatusKind.Loading}" /> <Condition Binding="{Binding Status.Kind}" Value="{x:Static local:EStatusKind.Loading}" />
</MultiDataTrigger.Conditions> </MultiDataTrigger.Conditions>
<Setter Property="Text" Value="{Binding Status.Label, StringFormat='{}{0} … ESC to Cancel'}" /> <Setter Property="Text" Value="{Binding Status.Label, StringFormat='{}{0} … ESC to Cancel'}" />
@ -805,7 +807,7 @@
<StatusBarItem.Style> <StatusBarItem.Style>
<Style TargetType="StatusBarItem"> <Style TargetType="StatusBarItem">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding IsAutoOpenSounds, Source={x:Static local:Settings.UserSettings.Default}}" Value="False"> <DataTrigger Binding="{Binding IsAutoOpenSounds, Source={x:Static settings:UserSettings.Default}}" Value="False">
<Setter Property="Visibility" Value="Hidden" /> <Setter Property="Visibility" Value="Hidden" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>

View File

@ -507,7 +507,7 @@ public class CUE4ParseViewModel : ViewModel
{ {
foreach (var asset in assetItems) foreach (var asset in assetItems)
{ {
Thread.Sleep(10); Thread.Yield();
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs); Extract(cancellationToken, asset.FullPath, TabControl.HasNoTabs);
} }
@ -517,7 +517,7 @@ public class CUE4ParseViewModel : ViewModel
{ {
foreach (var asset in folder.AssetsList.Assets) foreach (var asset in folder.AssetsList.Assets)
{ {
Thread.Sleep(10); Thread.Yield();
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
try try
{ {

View File

@ -1,4 +1,5 @@
using System.Diagnostics; using System.Collections.Generic;
using System.Diagnostics;
using System.Threading; using System.Threading;
using AdonisUI.Controls; using AdonisUI.Controls;
using FModel.Extensions; using FModel.Extensions;
@ -79,19 +80,13 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
case "ToolBox_Expand_All": case "ToolBox_Expand_All":
await ApplicationService.ThreadWorkerView.Begin(cancellationToken => await ApplicationService.ThreadWorkerView.Begin(cancellationToken =>
{ {
foreach (var folder in contextViewModel.CUE4Parse.AssetsFolder.Folders) SetFoldersIsExpanded(contextViewModel.CUE4Parse.AssetsFolder, true, cancellationToken);
{
LoopFolders(cancellationToken, folder, true);
}
}); });
break; break;
case "ToolBox_Collapse_All": case "ToolBox_Collapse_All":
await ApplicationService.ThreadWorkerView.Begin(cancellationToken => await ApplicationService.ThreadWorkerView.Begin(cancellationToken =>
{ {
foreach (var folder in contextViewModel.CUE4Parse.AssetsFolder.Folders) SetFoldersIsExpanded(contextViewModel.CUE4Parse.AssetsFolder, false, cancellationToken);
{
LoopFolders(cancellationToken, folder, false);
}
}); });
break; break;
case TreeItem selectedFolder: case TreeItem selectedFolder:
@ -101,15 +96,44 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
} }
} }
private void LoopFolders(CancellationToken cancellationToken, TreeItem parent, bool isExpanded) private static void SetFoldersIsExpanded(AssetsFolderViewModel root, bool isExpanded, CancellationToken cancellationToken)
{ {
if (parent.IsExpanded != isExpanded) LinkedList<TreeItem> nodes = new();
foreach (TreeItem folder in root.Folders)
{ {
parent.IsExpanded = isExpanded; nodes.AddLast(folder);
Thread.Sleep(10);
} }
cancellationToken.ThrowIfCancellationRequested(); LinkedListNode<TreeItem> current = nodes.First;
foreach (var f in parent.Folders) LoopFolders(cancellationToken, f, isExpanded); while (current != null)
{
TreeItem folder = current.Value;
// Collapse top-down (reduce layout updates)
if (!isExpanded)
{
folder.IsExpanded = isExpanded;
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
foreach (TreeItem child in folder.Folders)
{
nodes.AddLast(child);
}
current = current.Next;
}
// Expand bottom-up (reduce layout updates)
if (isExpanded)
{
for (LinkedListNode<TreeItem> node = nodes.Last; node != null; node = node.Previous)
{
node.Value.IsExpanded = isExpanded;
Thread.Yield();
cancellationToken.ThrowIfCancellationRequested();
}
}
} }
} }

View File

@ -6,6 +6,7 @@
xmlns:soundOut="clr-namespace:CSCore.SoundOut;assembly=CSCore" xmlns:soundOut="clr-namespace:CSCore.SoundOut;assembly=CSCore"
xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup" xmlns:audioControls="clr-namespace:FModel.Views.Resources.Controls.Aup"
xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters"
xmlns:settings="clr-namespace:FModel.Settings"
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit" xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:folding="clr-namespace:ICSharpCode.AvalonEdit.Folding;assembly=ICSharpCode.AvalonEdit" xmlns:folding="clr-namespace:ICSharpCode.AvalonEdit.Folding;assembly=ICSharpCode.AvalonEdit"
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI" xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
@ -182,19 +183,19 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}}" Value="{x:Static local:ELoadingMode.Single}"> <DataTrigger Binding="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}}" Value="{x:Static local:ELoadingMode.Single}">
<Setter Property="SelectionMode" Value="Single" /> <Setter Property="SelectionMode" Value="Single" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}}" Value="{x:Static local:ELoadingMode.Multiple}"> <DataTrigger Binding="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}}" Value="{x:Static local:ELoadingMode.Multiple}">
<Setter Property="SelectionMode" Value="Extended" /> <Setter Property="SelectionMode" Value="Extended" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}}" Value="{x:Static local:ELoadingMode.All}"> <DataTrigger Binding="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}}" Value="{x:Static local:ELoadingMode.All}">
<Setter Property="SelectionMode" Value="Extended" /> <Setter Property="SelectionMode" Value="Extended" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}}" Value="{x:Static local:ELoadingMode.AllButNew}"> <DataTrigger Binding="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}}" Value="{x:Static local:ELoadingMode.AllButNew}">
<Setter Property="SelectionMode" Value="Extended" /> <Setter Property="SelectionMode" Value="Extended" />
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{Binding LoadingMode, Source={x:Static local:Settings.UserSettings.Default}}" Value="{x:Static local:ELoadingMode.AllButModified}"> <DataTrigger Binding="{Binding LoadingMode, Source={x:Static settings:UserSettings.Default}}" Value="{x:Static local:ELoadingMode.AllButModified}">
<Setter Property="SelectionMode" Value="Extended" /> <Setter Property="SelectionMode" Value="Extended" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@ -247,7 +248,7 @@
</Style> </Style>
<Style x:Key="AssetsFolderTreeView" TargetType="TreeView" BasedOn="{StaticResource {x:Type TreeView}}"> <Style x:Key="AssetsFolderTreeView" TargetType="TreeView" BasedOn="{StaticResource {x:Type TreeView}}">
<Setter Property="ItemsSource" Value="{Binding CUE4Parse.AssetsFolder.FoldersView, IsAsync=True}" /> <Setter Property="ItemsSource" Value="{Binding CUE4Parse.AssetsFolder.FoldersView}" />
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="NeverExpand"/> <Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarExpansionMode" Value="NeverExpand"/>
<Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/> <Setter Property="adonisExtensions:ScrollViewerExtension.VerticalScrollBarPlacement" Value="Docked"/>
<Setter Property="adonisExtensions:ScrollViewerExtension.HorizontalScrollBarExpansionMode" Value="NeverExpand"/> <Setter Property="adonisExtensions:ScrollViewerExtension.HorizontalScrollBarExpansionMode" Value="NeverExpand"/>
@ -255,7 +256,7 @@
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ItemTemplate"> <Setter Property="ItemTemplate">
<Setter.Value> <Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding FoldersView, IsAsync=True}"> <HierarchicalDataTemplate ItemsSource="{Binding FoldersView}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image x:Name="TreeImage" Source="/FModel;component/Resources/folder.png" <Image x:Name="TreeImage" Source="/FModel;component/Resources/folder.png"
Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3.5 0" /> Width="16" Height="16" HorizontalAlignment="Center" Margin="0 0 3.5 0" />
@ -647,7 +648,7 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="{Binding AvalonImageSize, Source={x:Static local:Settings.UserSettings.Default}, Mode=TwoWay}" /> <ColumnDefinition Width="{Binding AvalonImageSize, Source={x:Static settings:UserSettings.Default}, Mode=TwoWay}" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" DataContext="{Binding SelectedItem, ElementName=TabControlName}" /> <controls:AvalonEditor x:Name="DynamicArea" Grid.Column="0" DataContext="{Binding SelectedItem, ElementName=TabControlName}" />