@@ -130,6 +130,104 @@ public AssetListViewMode ViewModeType
130130
131131 private FileSystemWatcher watcher ;
132132
133+ int lastKnownScrollPosition = 0 ;
134+
135+ private struct ScrollAnchor
136+ {
137+ public string Key ;
138+ public int Index ;
139+ public int Scroll ;
140+
141+ public ScrollAnchor ( string key , int index , int scroll )
142+ {
143+ Key = key ;
144+ Index = index ;
145+ Scroll = scroll ;
146+ }
147+ }
148+
149+ private bool TryGetItemKey ( object item , out string key )
150+ {
151+ key = item switch
152+ {
153+ AssetEntry ae => ae . FileInfo . FullName ?? ae . Asset ? . AbsolutePath ,
154+ DirectoryEntry de => de . DirectoryInfo . FullName ,
155+ _ => null
156+ } ;
157+
158+ return ! string . IsNullOrEmpty ( key ) ;
159+ }
160+
161+ private ScrollAnchor CaptureScrollAnchor ( )
162+ {
163+ if ( AssetList ? . VerticalScrollbar ? . IsValid ( ) == true )
164+ {
165+ lastKnownScrollPosition = AssetList . VerticalScrollbar . Value ;
166+ }
167+
168+ if ( AssetList ? . Items . Any ( ) != true )
169+ return new ScrollAnchor ( null , 0 , lastKnownScrollPosition ) ;
170+
171+ var itemHeight = MathF . Max ( AssetList . ItemSize . y + AssetList . ItemSpacing . y , 1f ) ;
172+ int index = ( int ) MathF . Floor ( lastKnownScrollPosition / itemHeight ) ;
173+ index = Math . Clamp ( index , 0 , AssetList . Items . Count ( ) - 1 ) ;
174+
175+ string key = null ;
176+ var item = AssetList . Items . ElementAt ( index ) ;
177+ TryGetItemKey ( item , out key ) ;
178+
179+ return new ScrollAnchor ( key , index , lastKnownScrollPosition ) ;
180+ }
181+
182+ private void RestoreScrollPosition ( int target )
183+ {
184+ if ( AssetList ? . VerticalScrollbar ? . IsValid ( ) != true )
185+ return ;
186+
187+ void Apply ( )
188+ {
189+ if ( AssetList ? . VerticalScrollbar ? . IsValid ( ) != true )
190+ return ;
191+
192+ var scrollbar = AssetList . VerticalScrollbar ;
193+ var clamped = Math . Clamp ( target , scrollbar . Minimum , scrollbar . Maximum ) ;
194+ scrollbar . Value = clamped ;
195+ AssetList . SmoothScrollTarget = 0 ;
196+ lastKnownScrollPosition = clamped ;
197+ }
198+
199+ Apply ( ) ;
200+ MainThread . Queue ( Apply ) ;
201+ }
202+
203+ private void RestoreScrollAnchor ( ScrollAnchor anchor )
204+ {
205+ if ( AssetList ? . VerticalScrollbar ? . IsValid ( ) != true )
206+ return ;
207+
208+ var desired = anchor . Scroll ;
209+
210+ if ( ! string . IsNullOrEmpty ( anchor . Key ) && AssetList . Items . Any ( ) )
211+ {
212+ var match = AssetList . Items . Select ( ( x , i ) => ( x , i ) )
213+ . FirstOrDefault ( tuple => TryGetItemKey ( tuple . x , out var key ) && StringComparer . OrdinalIgnoreCase . Equals ( key , anchor . Key ) ) ;
214+
215+ if ( match != default && match . i >= 0 )
216+ {
217+ var itemHeight = MathF . Max ( AssetList . ItemSize . y + AssetList . ItemSpacing . y , 1f ) ;
218+ desired = ( int ) ( match . i * itemHeight ) ;
219+ }
220+ }
221+ else if ( AssetList . Items . Any ( ) )
222+ {
223+ var idx = Math . Clamp ( anchor . Index , 0 , AssetList . Items . Count ( ) - 1 ) ;
224+ var itemHeight = MathF . Max ( AssetList . ItemSize . y + AssetList . ItemSpacing . y , 1f ) ;
225+ desired = ( int ) ( idx * itemHeight ) ;
226+ }
227+
228+ RestoreScrollPosition ( desired ) ;
229+ }
230+
133231 public AssetBrowser ( Widget parent ) : this ( parent , null )
134232 {
135233
@@ -410,7 +508,7 @@ private async Task<bool> UpdateAssetListAsync( bool recursive, CancellationToken
410508 List < object > items = new List < object > ( ) ;
411509 var tagCounts = new Dictionary < string , int > ( ) ;
412510
413- AssetList . Clear ( ) ;
511+ var anchor = CaptureScrollAnchor ( ) ;
414512
415513 await Task . Run ( ( ) =>
416514 {
@@ -501,6 +599,9 @@ await Task.Run( () =>
501599 return false ;
502600
503601 AssetList . SetItems ( items ) ;
602+
603+ RestoreScrollAnchor ( anchor ) ;
604+
504605 if ( ! string . IsNullOrEmpty ( lastSortColumn ) )
505606 {
506607 SortAssetList ( lastSortColumn , lastSortAscending ) ;
@@ -522,6 +623,7 @@ await Task.Run( () =>
522623
523624 private void SortAssetList ( string sortBy , bool ascending )
524625 {
626+ var anchor = CaptureScrollAnchor ( ) ;
525627 List < object > items ;
526628
527629 switch ( sortBy )
@@ -594,6 +696,8 @@ private void SortAssetList( string sortBy, bool ascending )
594696 items . Reverse ( ) ;
595697 }
596698 AssetList . SetItems ( items ) ;
699+ RestoreScrollAnchor ( anchor ) ;
700+ MainThread . Queue ( ( ) => RestoreScrollAnchor ( anchor ) ) ;
597701
598702 lastSortColumn = sortBy ;
599703 lastSortAscending = ascending ;
0 commit comments