Skip to content

Commit 08da0d5

Browse files
committed
Preserve scroll position in AssetBrowser refresh
1 parent 79eebb5 commit 08da0d5

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

game/addons/tools/Code/Editor/AssetBrowser/AssetBrowser.cs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)