Skip to content

Commit af9aa28

Browse files
authored
Merge pull request #22314 from unoplatform/unobot/release/stable/6.4/bp22218
fix: loopingselector fast scrolling skia (backport #22218)
2 parents de8d741 + b4e7355 commit af9aa28

File tree

2 files changed

+95
-4
lines changed

2 files changed

+95
-4
lines changed

src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TimePicker.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,80 @@ private Android.Widget.TimePicker FindTimePicker(Android.Views.View root)
423423
#endif
424424
#endif
425425

426+
[TestMethod]
427+
[GitHubWorkItem("https://github.com/unoplatform/uno/issues/21958")]
428+
public async Task When_Fast_Scrolling_Items_Should_Remain_Visible()
429+
{
430+
TimePicker timePicker = new()
431+
{
432+
Time = new TimeSpan(12, 30, 0)
433+
};
434+
#if HAS_UNO
435+
timePicker.UseNativeStyle = false;
436+
#endif
437+
TestServices.WindowHelper.WindowContent = timePicker;
438+
await TestServices.WindowHelper.WaitForLoaded(timePicker);
439+
440+
await DateTimePickerHelper.OpenDateTimePicker(timePicker);
441+
await TestServices.WindowHelper.WaitForIdle();
442+
443+
(var hourLoopingSelector, _, _) = await DateTimePickerHelper.GetHourMinutePeriodLoopingSelectorsFromOpenFlyout();
444+
445+
Assert.IsNotNull(hourLoopingSelector, "HourLoopingSelector should be found");
446+
447+
ScrollViewer scrollViewer = null;
448+
LoopingSelectorPanel panel = null;
449+
var initialRealizedItemCount = 0;
450+
451+
await TestServices.RunOnUIThread(() =>
452+
{
453+
scrollViewer = (ScrollViewer)VisualTreeUtils.FindVisualChildByName(hourLoopingSelector, "ScrollViewer");
454+
Assert.IsNotNull(scrollViewer, "ScrollViewer should be found");
455+
456+
panel = scrollViewer.Content as LoopingSelectorPanel;
457+
Assert.IsNotNull(panel, "LoopingSelectorPanel should be found");
458+
459+
initialRealizedItemCount = panel.Children.Count;
460+
Assert.IsGreaterThan(0, initialRealizedItemCount, "Should have realized items initially");
461+
});
462+
463+
// Simulate fast scrolling by rapidly changing the scroll position
464+
// This triggers multiple intermediate ViewChanged events
465+
var scrollPositions = new[] { 100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0 };
466+
467+
foreach (var scrollPosition in scrollPositions)
468+
{
469+
await TestServices.RunOnUIThread(() =>
470+
{
471+
scrollViewer.ChangeView(null, scrollPosition, null, disableAnimation: false);
472+
});
473+
474+
await Task.Delay(10); // Small delay to allow event processing
475+
476+
var currentItemCount = 0;
477+
await TestServices.RunOnUIThread(() =>
478+
{
479+
currentItemCount = panel.Children.Count;
480+
});
481+
482+
Assert.IsGreaterThan(0,
483+
currentItemCount, $"Should have visible items during fast scrolling at position {scrollPosition}, but found {currentItemCount} items");
484+
}
485+
486+
await TestServices.WindowHelper.WaitForIdle();
487+
488+
var finalRealizedItemCount = 0;
489+
await TestServices.RunOnUIThread(() =>
490+
{
491+
finalRealizedItemCount = panel.Children.Count;
492+
});
493+
494+
Assert.IsGreaterThan(0, finalRealizedItemCount, "Should have realized items after scrolling completes");
495+
496+
await ControlHelper.ClickFlyoutCloseButton(timePicker, false /* isAccept */);
497+
await TestServices.WindowHelper.WaitForIdle();
498+
}
499+
426500
class MyContext
427501
{
428502
public object StartTime => null;

src/Uno.UI/UI/Xaml/Controls/Primitives/LoopingSelector/LoopingSelector_Partial.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -421,10 +421,27 @@ void ProcessEvent()
421421
}
422422
}
423423

424-
// This event can be raised synchronously
425-
// with the ScrollViewer's ChangeView().
426-
// It must be delayed to prevent incorrect re-selection of another value.
427-
global::Windows.System.DispatcherQueue.GetForCurrentThread().TryEnqueue(ProcessEvent);
424+
// --------------------
425+
// Fix for fast scrolling issue where items disappear
426+
// --------------------
427+
// During intermediate scroll events (fast scrolling), we need to balance immediately
428+
// to ensure items remain visible. Only delay processing for final view changes
429+
// to prevent incorrect re-selection.
430+
if (pEventArgs.IsIntermediate)
431+
{
432+
// For intermediate events, balance immediately to keep items visible during fast scrolling
433+
if (!_isWithinScrollChange && !_isWithinArrangeOverride)
434+
{
435+
Balance(false);
436+
}
437+
}
438+
else
439+
{
440+
// This event can be raised synchronously
441+
// with the ScrollViewer's ChangeView().
442+
// It must be delayed to prevent incorrect re-selection of another value.
443+
global::Windows.System.DispatcherQueue.GetForCurrentThread().TryEnqueue(ProcessEvent);
444+
}
428445
}
429446

430447
void OnViewChanging(

0 commit comments

Comments
 (0)