From 70b7e56eb7082dd1bbb3354c5e98933b4c734d46 Mon Sep 17 00:00:00 2001 From: Misaki Date: Sun, 29 Mar 2026 14:54:26 +0900 Subject: [PATCH] feat(editor): implement scroll state saving for ProjectBrowser ItemsView --- .../View/Controls/ProjectBrowser.xaml.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/Editor/Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs b/src/Editor/Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs index 5e50c2f..7eb931d 100644 --- a/src/Editor/Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs +++ b/src/Editor/Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs @@ -17,6 +17,9 @@ internal sealed partial class ProjectBrowser : UserControl private readonly IInspectorService _inspectorService; private bool _isUpdatingSelection = false; + private double _savedHorizontalOffset; + private double _savedVerticalOffset; + private ScrollViewer? _filesScrollViewer; public ProjectBrowserViewModel ViewModel { @@ -50,12 +53,42 @@ internal sealed partial class ProjectBrowser : UserControl { _inspectorService.OnSelectionChanged += _inspectorService_OnSelectionChanged; PART_FilesView.UpdateLayout(); + + DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () => + { + if (_filesScrollViewer == null) + { + _filesScrollViewer = FindVisualChild(PART_FilesView); + } + + if (_filesScrollViewer != null) + { + // Sometimes resetting the ItemsSource helps clear corrupted virtualization state + var itemsSource = PART_FilesView.ItemsSource; + PART_FilesView.ItemsSource = null; + PART_FilesView.ItemsSource = itemsSource; + + PART_FilesView.UpdateLayout(); + _filesScrollViewer.ChangeView(_savedHorizontalOffset, _savedVerticalOffset, null, true); + } + }); } private void ProjectBrowser_Unloaded(object sender, RoutedEventArgs e) { _inspectorService.OnSelectionChanged -= _inspectorService_OnSelectionChanged; + if (_filesScrollViewer == null) + { + _filesScrollViewer = FindVisualChild(PART_FilesView); + } + + if (_filesScrollViewer != null) + { + _savedHorizontalOffset = _filesScrollViewer.HorizontalOffset; + _savedVerticalOffset = _filesScrollViewer.VerticalOffset; + } + if (LastFocused == this) { LastFocused = null; @@ -71,6 +104,26 @@ internal sealed partial class ProjectBrowser : UserControl } } + private static T? FindVisualChild(DependencyObject parent) where T : DependencyObject + { + for (int i = 0; i < Microsoft.UI.Xaml.Media.VisualTreeHelper.GetChildrenCount(parent); i++) + { + var child = Microsoft.UI.Xaml.Media.VisualTreeHelper.GetChild(parent, i); + if (child is T t) + { + return t; + } + + var result = FindVisualChild(child); + if (result != null) + { + return result; + } + } + + return null; + } + private void PART_DirectoriesView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args) { if (_isUpdatingSelection)