using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; using MatterHackers.GuiAutomation; using MatterHackers.MatterControl; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.PartPreviewWindow; using MatterHackers.MatterControl.Tests.Automation; using MatterHackers.VectorMath; using NUnit.Framework; namespace MatterControl.Tests.MatterControl { [TestFixture, Category("PopupAnchorTests"), RunInApplicationDomain, Apartment(ApartmentState.STA)] public class PopupAnchorTests { [SetUp] public void TestSetup() { StaticData.RootPath = TestContext.CurrentContext.ResolveProjectPath(4, "StaticData"); MatterControlUtilities.OverrideAppDataLocation(TestContext.CurrentContext.ResolveProjectPath(4)); } [Test] public async Task WindowTest() { var systemWindow = new PopupsTestWindow(700, 300) { Details = new Dictionary() { ["Task"] = "General Popup", ["Expected"] = "Popup should appear on click" } }; await systemWindow.RunTest(testRunner => { systemWindow.Padding = systemWindow.Padding.Clone(bottom: 180); var button = new TextButton("Popup", systemWindow.Theme) { Name = "targetA", VAnchor = VAnchor.Bottom, HAnchor = HAnchor.Left, }; systemWindow.AddChild(button); var color = Color.LightBlue; button.Click += (s, e) => { systemWindow.ShowPopup( new MatePoint() { Widget = button }, new MatePoint() { Widget = new GuiWidget(180d, 100d) { BackgroundColor = color, Border = 2, BorderColor = color.Blend(Color.Black, 0.4) } }); }; testRunner.ClickByName("targetA"); testRunner.Delay(); return Task.CompletedTask; }, 30); } [Test] public async Task TopBottomPopupTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup bottoms should align with button top" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom; Assert.AreEqual(buttonPosition, popupPosition); }); } [Test] public async Task TopTopPopupTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup tops should align with button top" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top; Assert.AreEqual(buttonPosition, popupPosition); }); } [Test] public async Task BottomTopPopupTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup tops should align with button bottom" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Right, MateEdge.Bottom) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top; Assert.AreEqual(buttonPosition, popupPosition); }); } [Test] public async Task BottomBottomPopupTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Bottom-Bottom Tests", ["Expected"] = "Popup bottoms should align with button bottom" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom; Assert.AreEqual(buttonPosition, popupPosition); }); } // Redirect down to up [Test] public async Task BottomTopUpRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup tops should align with button bottom" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top), AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom; Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { row.VAnchor = VAnchor.Bottom | VAnchor.Fit; }); } [Test] public async Task TopTopUpRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup tops should align with button top" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top), AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top), AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Bottom; Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { row.VAnchor = VAnchor.Bottom | VAnchor.Fit; }); } // Redirect up to down [Test] public async Task BottomTopDownRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup bottoms should align with button top" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top), AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom), }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Left, MateEdge.Top), }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Bottom; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top; Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { row.VAnchor = VAnchor.Top | VAnchor.Fit; }); } [Test] public async Task TopTopDownRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Bottom-Bottom Tests", ["Expected"] = "Popup bottoms should align with button bottom" } }; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Right, MateEdge.Top), }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Left, MateEdge.Top), }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Top; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Top; Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { row.VAnchor = VAnchor.Top | VAnchor.Fit; }); } // Redirect left to right [Test] public async Task LeftRightRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Top-Bottom Tests", ["Expected"] = "Popup tops should align with button bottom" } }; systemWindow.Padding = systemWindow.Padding.Clone(left: 0); int i = 0; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Left, MateEdge.Top) }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Top), AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom) }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Left; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Left; if (i++ > 2) { // Switch to anchor right aligned for the last case buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Right; } Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { // Clear left margin so menus clip row.Margin = 2; }); } // Redirect right to left [Test] public async Task RightLeftRedirectTest() { var systemWindow = new PopupsTestWindow(800, 600) { Details = new Dictionary() { ["Task"] = "Bottom-Bottom Tests", ["Expected"] = "Popup bottoms should align with button bottom" } }; int i = 0; await AnchorTests( systemWindow, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Left, MateEdge.Top), }, new MatePoint() { Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom), AltMate = new MateOptions(MateEdge.Right, MateEdge.Top), }, new TextButton("Popup", systemWindow.Theme) { Name = "buttonA", VAnchor = VAnchor.Bottom, }, (buttonWidget, popupWidget) => { double buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Left; double popupPosition = popupWidget.TransformToScreenSpace(popupWidget.LocalBounds).Right; if (i++ == 2) { // Switch to anchor right aligned for the last case buttonPosition = buttonWidget.TransformToScreenSpace(buttonWidget.LocalBounds).Right; } Assert.AreEqual(buttonPosition, popupPosition); }, (row) => { row.HAnchor = HAnchor.Right | HAnchor.Fit; row.VAnchor = VAnchor.Center | VAnchor.Fit; }); } private static async Task AnchorTests(PopupsTestWindow systemWindow, MatePoint anchor, MatePoint popup, TextButton button, Action validator, Action rowAdjuster = null) { await systemWindow.RunTest(testRunner => { button.BackgroundColor = Color.LightGray; button.HoverColor = Color.LightBlue; button.MouseDownColor = Color.Magenta; var row = new FlowLayoutWidget() { VAnchor = VAnchor.Center | VAnchor.Fit, HAnchor = HAnchor.Left | HAnchor.Fit, Margin = new BorderDouble(left: 120) }; systemWindow.AddChild(row); row.AddChild(button); rowAdjuster?.Invoke(row); button.Click += (s, e) => { popup.Widget = new GuiWidget(180d, 100d) { BackgroundColor = Color.LightBlue, Border = 2, BorderColor = Color.LightBlue.Blend(Color.Black, 0.4) }; systemWindow.ShowPopup(anchor, popup); }; anchor.Widget = button; for (var i = 0; i < 4; i++) { switch (i) { case 0: anchor.Mate.HorizontalEdge = MateEdge.Left; popup.Mate.HorizontalEdge = MateEdge.Right; break; case 1: anchor.Mate.HorizontalEdge = MateEdge.Left; popup.Mate.HorizontalEdge = MateEdge.Left; break; case 2: anchor.Mate.HorizontalEdge = MateEdge.Right; popup.Mate.HorizontalEdge = MateEdge.Right; break; case 3: anchor.Mate.HorizontalEdge = MateEdge.Right; popup.Mate.HorizontalEdge = MateEdge.Left; break; } testRunner.ClickByName("buttonA"); testRunner.Delay(); validator.Invoke(button, popup.Widget); popup.Widget.Unfocus(); } testRunner.Delay(); return Task.CompletedTask; }, 25); } public class PopupsTestWindow : SystemWindow { private FlowLayoutWidget column; public ThemeConfig Theme { get; } public PopupsTestWindow(int width, int height) : base(width, height) { this.BackgroundColor = new Color(56, 56, 56); Theme = new ThemeConfig(); this.Padding = new BorderDouble(left: 120, bottom: 10, right: 10, top: 10); column = new FlowLayoutWidget(FlowDirection.TopToBottom) { HAnchor = HAnchor.Right | HAnchor.Fit, VAnchor = VAnchor.Stretch, }; this.AddChild(column); } private Dictionary _details; public Dictionary Details { get => _details; set { _details = value; foreach (var kvp in value) { this.ShowDetails(kvp.Key, kvp.Value); } } } public void ShowDetails(string heading, string text) { // Store var row = new FlowLayoutWidget { VAnchor = VAnchor.Fit, HAnchor = HAnchor.Left | HAnchor.Fit }; column.AddChild(row); row.AddChild(new TextWidget(heading + ":", textColor: Color.White, pointSize: 9) { MinimumSize = new Vector2(80, 0) }); row.AddChild(new TextWidget(text, textColor: Color.White, pointSize: 9)); } } } }