diff --git a/CUE4Parse b/CUE4Parse index cfff57a0..cb72c6e1 160000 --- a/CUE4Parse +++ b/CUE4Parse @@ -1 +1 @@ -Subproject commit cfff57a0483501e0fa555dff5bf542748afe0bab +Subproject commit cb72c6e181687bc9b748ba09928677763057dd3a diff --git a/FModel/MainWindow.xaml b/FModel/MainWindow.xaml index 614b866e..cc5becb0 100644 --- a/FModel/MainWindow.xaml +++ b/FModel/MainWindow.xaml @@ -4,6 +4,7 @@ xmlns:local="clr-namespace:FModel" xmlns:viewModels="clr-namespace:FModel.ViewModels" xmlns:controls="clr-namespace:FModel.Views.Resources.Controls" + xmlns:explorer="clr-namespace:FModel.Views.Resources.Controls.Explorer" xmlns:inputs="clr-namespace:FModel.Views.Resources.Controls.Inputs" xmlns:converters="clr-namespace:FModel.Views.Resources.Converters" xmlns:settings="clr-namespace:FModel.Settings" @@ -558,7 +559,7 @@ DataContext="{Binding CUE4Parse.AssetsFolder.SelectedFolder.PathAtThisPoint}" /> - @@ -574,9 +575,9 @@ - + - @@ -594,7 +595,7 @@ - + diff --git a/FModel/MainWindow.xaml.cs b/FModel/MainWindow.xaml.cs index a8c98f06..2202edb9 100644 --- a/FModel/MainWindow.xaml.cs +++ b/FModel/MainWindow.xaml.cs @@ -271,21 +271,17 @@ public partial class MainWindow if (sender is not ItemContainerGenerator { Status: GeneratorStatus.ContainersGenerated } generator) return; - var foundVisibleItem = false; - var itemCount = generator.Items.Count; + var list = ReferenceEquals(generator, AssetsExplorer.ItemContainerGenerator) ? AssetsExplorer + : ReferenceEquals(generator, AssetsListExplorer.ItemContainerGenerator) ? AssetsListExplorer + : AssetsListName; - for (var i = 0; i < itemCount; i++) + if (!list.IsVisible || list.FindVisualChild() is not { } panel) + return; + + foreach (var container in panel.Children.OfType()) { - var container = generator.ContainerFromIndex(i); - if (container == null) + if (container is FrameworkElement { IsVisible: true, DataContext: GameFileViewModel file }) { - if (foundVisibleItem) break; // we're past the visible range already - continue; // keep scrolling to find visible items - } - - if (container is FrameworkElement { IsVisible: true } && generator.Items[i] is GameFileViewModel file) - { - foundVisibleItem = true; file.OnIsVisible(); } } @@ -303,7 +299,6 @@ public partial class MainWindow public void SelectFolder(TreeItem folder) { _applicationView.SelectedLeftTabIndex = 1; - UpdateLayout(); AssetsFolderName.SelectFolder(folder); AssetsFolderName.FocusSelection(); } diff --git a/FModel/ViewModels/AssetsFolderViewModel.cs b/FModel/ViewModels/AssetsFolderViewModel.cs index 44fb35cd..43d4b964 100644 --- a/FModel/ViewModels/AssetsFolderViewModel.cs +++ b/FModel/ViewModels/AssetsFolderViewModel.cs @@ -101,9 +101,9 @@ public class TreeItem : ViewModel { get { + // BulkPopulate sorts Folders before this view is created _filteredFoldersView ??= new ListCollectionView(Folders) { - SortDescriptions = { new SortDescription(nameof(Header), ListSortDirection.Ascending) }, Filter = e => ItemFilter(e, SearchText.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries)) }; return _filteredFoldersView; diff --git a/FModel/Views/Resources/Controls/Explorer/AssetExplorerListBox.cs b/FModel/Views/Resources/Controls/Explorer/AssetExplorerListBox.cs new file mode 100644 index 00000000..b3ea662c --- /dev/null +++ b/FModel/Views/Resources/Controls/Explorer/AssetExplorerListBox.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using FModel.Extensions; + +namespace FModel.Views.Resources.Controls.Explorer; + +public class AssetExplorerListBox : ListBox +{ + protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) + { + base.OnItemsSourceChanged(oldValue, newValue); + + // Always start at the top, otherwise inherited offset can cause items not to be visible when we change folder + if (this.FindVisualChild() is IScrollInfo scroll) + { + scroll.SetVerticalOffset(0); + } + } +} diff --git a/FModel/Views/Resources/Controls/Explorer/Resources.xaml b/FModel/Views/Resources/Controls/Explorer/Resources.xaml index f218f65e..95b5e7bd 100644 --- a/FModel/Views/Resources/Controls/Explorer/Resources.xaml +++ b/FModel/Views/Resources/Controls/Explorer/Resources.xaml @@ -9,6 +9,16 @@ xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI" xmlns:adonisConverters="clr-namespace:AdonisUI.Converters;assembly=AdonisUI" x:Class="FModel.Views.Resources.Controls.TiledExplorer.ResourcesDictionary"> + + + + + + + + + + @@ -69,14 +79,7 @@ - - - - - - - + @@ -201,7 +204,7 @@ - + - + diff --git a/FModel/Views/Resources/Controls/Explorer/Resources.xaml.cs b/FModel/Views/Resources/Controls/Explorer/Resources.xaml.cs index 0951a8b2..31ce1002 100644 --- a/FModel/Views/Resources/Controls/Explorer/Resources.xaml.cs +++ b/FModel/Views/Resources/Controls/Explorer/Resources.xaml.cs @@ -44,7 +44,7 @@ public partial class ResourcesDictionary { if (sender is not ListBoxItem item) return; - if (item.DataContext is not GameFileViewModel) + if (item.DataContext is not (GameFileViewModel or TreeItem)) return; var listBox = ItemsControl.ItemsControlFromItemContainer(item) as ListBox; if (listBox == null) @@ -57,9 +57,20 @@ public partial class ResourcesDictionary } item.Focus(); - var contextMenu = listBox.FindResource("FileContextMenu") as ContextMenu; + var isFolder = item.DataContext is TreeItem; + var contextMenu = listBox.FindResource(isFolder ? "FolderContextMenu" : "FileContextMenu") as ContextMenu; if (contextMenu is not null) { + if (isFolder) + { + // Create FolderContextMenu only on right-click + listBox.ContextMenu = null; + contextMenu.PlacementTarget = item; + contextMenu.IsOpen = true; + e.Handled = true; + return; + } + listBox.ContextMenu = null; item.Dispatcher.BeginInvoke(new Action(() => { diff --git a/FModel/Views/Resources/Controls/Explorer/TiledExplorer/FolderButton.xaml b/FModel/Views/Resources/Controls/Explorer/TiledExplorer/FolderButton.xaml index bd545f03..5bc7bf12 100644 --- a/FModel/Views/Resources/Controls/Explorer/TiledExplorer/FolderButton.xaml +++ b/FModel/Views/Resources/Controls/Explorer/TiledExplorer/FolderButton.xaml @@ -20,36 +20,13 @@ - - - - - - - - - - - - - - - - diff --git a/FModel/Views/Resources/Controls/FolderTree.cs b/FModel/Views/Resources/Controls/FolderTree.cs index 216bdb1c..ef169303 100644 --- a/FModel/Views/Resources/Controls/FolderTree.cs +++ b/FModel/Views/Resources/Controls/FolderTree.cs @@ -12,6 +12,9 @@ public sealed class FolderTree : ListBox { public static readonly DependencyProperty FoldersProperty = DependencyProperty.Register(nameof(Folders), typeof(AssetsFolderViewModel), typeof(FolderTree)); + public static readonly RoutedEvent ExpandArrowEvent = EventManager.RegisterRoutedEvent("ExpandArrow", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(FolderTree)); + public static readonly RoutedEvent CollapseArrowEvent = EventManager.RegisterRoutedEvent("CollapseArrow", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(FolderTree)); + public AssetsFolderViewModel Folders { get => (AssetsFolderViewModel) GetValue(FoldersProperty); @@ -58,12 +61,28 @@ public sealed class FolderTree : ListBox } } + private void ToggleFolder(TreeItem folder) + { + if (Folders == null) + return; + + var wasExpanded = folder.IsExpanded; + Folders.Toggle(folder); + if (folder.IsExpanded == wasExpanded) + return; + + if (ItemContainerGenerator.ContainerFromItem(folder) is ListBoxItem row) + { + row.FindVisualChild()?.RaiseEvent(new RoutedEventArgs(folder.IsExpanded ? ExpandArrowEvent : CollapseArrowEvent)); + } + } + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) { if (e.OriginalSource is DependencyObject source && source.FindAncestor() is { DataContext: TreeItem folder } && ContainerFromElement(this, source) is ListBoxItem) { e.Handled = true; - Folders?.Toggle(folder); + ToggleFolder(folder); return; } @@ -77,7 +96,7 @@ public sealed class FolderTree : ListBox e.Handled = true; if (folder.Folders.Count > 0) { - Folders?.Toggle(folder); + ToggleFolder(folder); } else if (folder.AssetsList.Assets.Count > 0) { @@ -113,7 +132,7 @@ public sealed class FolderTree : ListBox case Key.Left: if (folder.IsExpanded) { - Folders?.Toggle(folder); + ToggleFolder(folder); } else if (folder.Parent != null) { @@ -123,7 +142,7 @@ public sealed class FolderTree : ListBox case Key.Right: if (!folder.IsExpanded) { - Folders?.Toggle(folder); + ToggleFolder(folder); } else if (folder.Folders.Count > 0) { @@ -144,7 +163,7 @@ public sealed class FolderTree : ListBox SelectFolder(folder); if (!folder.IsExpanded) { - Folders?.Toggle(folder); + ToggleFolder(folder); } break; default: diff --git a/FModel/Views/Resources/Resources.xaml b/FModel/Views/Resources/Resources.xaml index 52437c7e..cf1fb7ca 100644 --- a/FModel/Views/Resources/Resources.xaml +++ b/FModel/Views/Resources/Resources.xaml @@ -311,33 +311,30 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/FModel/Views/Snooper/Models/StaticModel.cs b/FModel/Views/Snooper/Models/StaticModel.cs index d0fba195..42955d7c 100644 --- a/FModel/Views/Snooper/Models/StaticModel.cs +++ b/FModel/Views/Snooper/Models/StaticModel.cs @@ -56,7 +56,7 @@ public class StaticModel : UModel Box = staticMesh.Bounds * 1.5f * Constants.SCALE_DOWN_RATIO; } - public StaticModel(UPaperSprite paperSprite, UTexture2D texture) : base(paperSprite) + public StaticModel(UPaperSprite paperSprite) : base(paperSprite) { Indices = new uint[paperSprite.BakedRenderData.Length]; for (int i = 0; i < Indices.Length; i++) @@ -97,7 +97,7 @@ public class StaticModel : UModel { Materials[0] = new Material(); } - Materials[0].Parameters.Textures[CMaterialParams2.FallbackDiffuse] = texture; + Materials[0].Parameters.Textures[CMaterialParams2.FallbackDiffuse] = paperSprite.BakedSourceTexture; Materials[0].IsUsed = true; Sections = new Section[1]; diff --git a/FModel/Views/Snooper/Renderer.cs b/FModel/Views/Snooper/Renderer.cs index baaf5b93..bbb0bd79 100644 --- a/FModel/Views/Snooper/Renderer.cs +++ b/FModel/Views/Snooper/Renderer.cs @@ -460,7 +460,7 @@ public class Renderer : IDisposable return; } - Options.Models[guid] = new StaticModel(original, texture); + Options.Models[guid] = new StaticModel(original); Options.SelectModel(guid); } @@ -804,8 +804,8 @@ public class Renderer : IDisposable private void WorldTextureData(Material material, UObject textureData, string name, string key) { - if (textureData.TryGetValue(out FPackageIndex package, name) && package.Load() is UTexture2D texture) - material.Parameters.Textures[key] = texture; + if (textureData.TryGetValue(out FPackageIndex package, name)) + material.Parameters.Textures[key] = package; } private void AdditionalWorlds(UObject actor, Matrix4x4 relation, CancellationToken cancellationToken)