Skip to content

Commit

Permalink
Merge pull request #13409 from ramezgerges/listview_selected_background
Browse files Browse the repository at this point in the history
fix(listview): miscellaneous fixes to recycled containers
  • Loading branch information
jeromelaban authored Sep 11, 2023
2 parents 75ecbf2 + 429773e commit 6e569bc
Show file tree
Hide file tree
Showing 6 changed files with 733 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
Expand Down Expand Up @@ -37,6 +38,7 @@
#endif

using static Private.Infrastructure.TestServices;
using Point = Windows.Foundation.Point;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls
{
Expand Down Expand Up @@ -1419,7 +1421,7 @@ public async Task When_Large_List_Scroll_To_End_Then_Back_Up_And_First_Item()
await Task.Delay(200);
await WindowHelper.WaitForIdle();

ScrollTo(list, 5); // Scroll to end
ScrollTo(list, 5); // Scroll back up

await Task.Delay(200);
await WindowHelper.WaitForIdle();
Expand All @@ -1436,6 +1438,373 @@ public async Task When_Large_List_Scroll_To_End_Then_Back_Up_And_First_Item()
}
}

[TestMethod]
[RunsOnUIThread]
#if __IOS__ || __ANDROID__
[Ignore("Disabled because of animated scrolling, even when explicitly requested.")]
#elif __WASM__
[Ignore("Flaky in CI.")]
#endif
public async Task When_Large_List_Scroll_To_End_Then_Back_Up_And_First_Item2()
{
var container = new Grid { Height = 500, Width = 100 };

var list = new ListView
{
ItemContainerStyle = NoSpaceContainerStyle,
ItemTemplate = new DataTemplate(() =>
{
var tb = new TextBlock();
tb.SetBinding(TextBlock.TextProperty, new Binding());
var border = new Border()
{
Height = 50,
Child = tb
};

return border;
})
};
container.Children.Add(list);

var source = new List<string>();

var random = new Random(42);
string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

for (var i = 0; i < 100; i++)
{
source.Add(RandomString(5));
}
list.ItemsSource = source;

WindowHelper.WindowContent = container;
await WindowHelper.WaitForIdle();

ScrollTo(list, 1000000); // Scroll to end

await Task.Delay(200);
await WindowHelper.WaitForIdle();

ScrollTo(list, 50); // scroll back up but not all the way
ScrollTo(list, 0);

await Task.Delay(200);
await WindowHelper.WaitForIdle();

var firstContainer = (FrameworkElement)list.ContainerFromIndex(0);

firstContainer.Should().NotBeNull();
LayoutInformation.GetLayoutSlot(firstContainer).Y.Should().BeLessOrEqualTo(0);

var secondContainer = (FrameworkElement)list.ContainerFromIndex(1);

secondContainer.Should().NotBeNull();
LayoutInformation.GetLayoutSlot(secondContainer).Y.Should().Be(50);
}

[TestMethod]
[RunsOnUIThread]
#if !__SKIA__
[Ignore("InputInjector is only supported on skia")]
#endif
public async Task When_Large_List_Scroll_To_End_Then_Back_Up_TryClick()
{
var container = new Grid { Height = 500, Width = 100 };

var list = new ListView
{
ItemTemplate = new DataTemplate(() =>
{

var tb = new TextBlock();
tb.SetBinding(TextBlock.TextProperty, new Binding());
var border = new Border()
{
Height = 50,
Child = tb
};

return border;
})
};
container.Children.Add(list);

var source = new List<string>();

var random = new Random(42);
string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}

for (var i = 0; i < 100; i++)
{
source.Add(RandomString(5));
}
list.ItemsSource = source;

WindowHelper.WindowContent = container;
await WindowHelper.WaitForIdle();

var injector = InputInjector.TryCreate() ?? throw new InvalidOperationException("Failed to init the InputInjector");
var mouse = injector.GetMouse();

var bounds = list.GetAbsoluteBounds();
mouse.MoveTo(new Point(bounds.GetMidX(), bounds.Top + 10));
mouse.Press();
mouse.Release();

await Task.Delay(200);
await WindowHelper.WaitForIdle();

// This problem is incredibly difficult to reproduce. These are the exact
// mouse wheel deltas that my trackpad produced when I reproduced this manually.
var deltas = new[]
{
-266,
-393,
-626,
-730,
-410,
-644,
-666,
-328,
-584,
-671,
-535,
-250,
-460,
-514,
-538,
-382,
-692,
-342,
-557,
-331,
-540,
-320,
-523,
-385,
-428,
-397,
-388,
-143,
-382,
-370,
-366,
-358,
-566,
-128,
-545,
-121,
-540,
-97,
-536,
-55,
-476,
-246,
-344,
-203,
-311,
-190,
-295,
-225,
-234,
-86,
-209,
-26,
-389,
-189,
-329,
-57,
-282,
-130,
-205,
-108,
-183,
-106,
-172,
-102,
-86,
-75,
-108,
-112,
-64,
-24,
-20,
-20,
-13,
-11,
-7,
-4,
-5,
-2,
231,
122,
164,
266,
887,
167,
565,
434,
382,
372,
344,
170,
269,
547,
158,
272,
434,
168,
253,
386,
154,
269,
244,
238,
399,
91,
374,
153,
282,
143,
225,
194,
60,
183,
154,
262,
60,
249,
55,
232,
46,
168,
37,
128,
55,
82,
37,
68,
27,
35,
27,
17,
13,
12,
2,
6,
3,
2,
};

foreach (var delta in deltas.Where(i => i < 0))
{
mouse.Wheel(delta);
}

await Task.Delay(200);
await WindowHelper.WaitForIdle();

mouse.MoveTo(new Point(bounds.GetMidX(), bounds.Top + 220));
mouse.Press();
mouse.Release();

await Task.Delay(200);
await WindowHelper.WaitForIdle();

foreach (var delta in deltas.Where(i => i > 0))
{
mouse.Wheel(delta);
await WindowHelper.WaitForIdle();
}

mouse.MoveTo(new Point(bounds.GetMidX(), bounds.Top + 10));
mouse.Press();
mouse.Release();

await Task.Delay(200);
await WindowHelper.WaitForIdle();

mouse.MoveTo(new Point(bounds.GetMidX(), bounds.Top + 60));
mouse.Press();
mouse.Release();

await Task.Delay(200);
await WindowHelper.WaitForIdle();

// The second container should be selected and nothing else.
// Trying to check for this with references could be misleading,
// since this is originally a virtualization issue and references
// could be to different things than those shown on the screen.
var si = await UITestHelper.ScreenShot(list, true);
ImageAssert.HasColorAt(si, 70, 65, Colors.FromARGB("#66AEE7")); // selected

// check starting from below the second item that nothing looks selected or hovered
ImageAssert.DoesNotHaveColorInRectangle(si, new Rectangle(100, 110, si.Width - 100, si.Height - 110), Colors.FromARGB("#66AEE7")); // selected
ImageAssert.DoesNotHaveColorInRectangle(si, new Rectangle(100, 110, si.Width - 100, si.Height - 110), Colors.FromARGB("#FFE6E6E6")); // hovered
}

[TestMethod]
[RunsOnUIThread]
#if !__CROSSRUNTIME__
[Ignore("Native listviews differ in virtualization mechanics")]
#endif
public async Task ListView_ObservableCollection_Creation_Count()
{
var SUT = new ListView_ObservableCollection_CreationCount();

WindowHelper.WindowContent = SUT;
await WindowHelper.WaitForIdle();

await AdvanceAutomation("Added");
await AdvanceAutomation("Scrolled1");

var expectedTemplateCreationCount = GetTemplateCreationCount();
//var expectedTemplateBindCount = GetTemplateBindCount(); // For some reason WASM performs extra bindings on scrolling
var expectedContainerCreationCount = GetContainerCreationCount();

await AdvanceAutomation("Scrolled2");

Assert.AreEqual(expectedTemplateCreationCount, GetTemplateCreationCount());
Assert.AreEqual(expectedContainerCreationCount, GetContainerCreationCount());

var expectedTemplateBindCount = GetTemplateBindCount();

await AdvanceAutomation("Added above");

Assert.AreEqual(expectedTemplateCreationCount, GetTemplateCreationCount());
Assert.AreEqual(expectedContainerCreationCount, GetContainerCreationCount());
Assert.AreEqual(expectedTemplateBindCount, GetTemplateBindCount()); // Note: this doesn't actually seem to be the case on Windows - the bind count increases for some reason

await AdvanceAutomation("Removed above");

Assert.AreEqual(expectedTemplateCreationCount, GetTemplateCreationCount());
Assert.AreEqual(expectedContainerCreationCount, GetContainerCreationCount());
Assert.AreEqual(expectedTemplateBindCount, GetTemplateBindCount());

int GetTemplateCreationCount() => int.Parse(((TextBlock)SUT.FindName("CreationCountText")).Text);
int GetTemplateBindCount() => int.Parse(((TextBlock)SUT.FindName("BindCountText")).Text);
int GetContainerCreationCount() => int.Parse(((TextBlock)SUT.FindName("CreationCount2Text")).Text);

async Task AdvanceAutomation(string automationStep)
{
var button = (Button)SUT.FindName("AutomateButton");
button.RaiseClick();
await WindowHelper.WaitFor(() => ((TextBlock)SUT.FindName("AutomationStepTextBlock")).Text == automationStep);
await WindowHelper.WaitForIdle();
}
}

[TestMethod]
[RunsOnUIThread]
#if __IOS__ || __ANDROID__
Expand Down
Loading

0 comments on commit 6e569bc

Please sign in to comment.