using Annulus; using Azimuth; using Clave; using System; using System.Collections.Generic; using UnitTestSharp; namespace Glass.UnitTests { public class PopupMenuTests : TestFixture { [Flags] public enum TestChoice { First = 1, Second = 2, Third = 4, } public enum NotFlags { Only = 1, } /// /// Spacing of 4 and a 50 by 20 item for each of the first three bits. /// public static readonly PopupMenuMaterial Measurements = new PopupMenuMaterial { Background = null, Spacing = 4, Items = new[] { ItemOf(46, 16), ItemOf(46, 16), ItemOf(46, 16) }, }; /// /// An item whose body wants exactly this much room, inside a 2 thick border. /// public static ButtonMaterial ItemOf(Scalar width, Scalar height) { Material body = new ScrollViewTests.SizedSolidMaterial(new Vector(width, height)); return new ButtonMaterial(ScrollViewTests.Bordered(2, "item").Framing(body)); } public static Rect Box(Scalar x, Scalar y, Scalar width, Scalar height) { return Rect.FromPositionAndSize(new Vector(x, y), new Vector(width, height)); } public static PopupMenu NewMenu( TestChoice choices = TestChoice.First | TestChoice.Second | TestChoice.Third, PopupMenuMaterial? material = null) { var returnMe = new PopupMenu(choices, DialogBehavior.Default); returnMe.PopupMenuMaterial = material ?? Measurements; return returnMe; } public static PopupMenu OpenedAt(Vector position, Vector? screenSize = null) { PopupMenu returnMe = NewMenu(); returnMe.OpenAt(position); PopupTests.Tick(returnMe, Box(0, 0, 10, 10), screenSize); return returnMe; } public static void Click(Button item) { item.Click(default, MouseButtonFlags.Left, 1, NoKeys.Held, NoKeys.Manager); } public class CtorTests : TestFixture { public void Always_BuildsAnItemForEachBitAskedFor() { var menu = new PopupMenu(TestChoice.First | TestChoice.Third, DialogBehavior.Default); CheckNotNull(menu.ItemFor(TestChoice.First)); CheckNotNull(menu.ItemFor(TestChoice.Third)); } public void Always_StartsClosed() { CheckFalse(NewMenu().IsOpen); } public void AChoiceNotAskedFor_HasNoItem() { var menu = new PopupMenu(TestChoice.First, DialogBehavior.Default); CheckThrow(typeof(ArgumentException)); menu.ItemFor(TestChoice.Second); } public void NoChoices_Throws() { CheckThrow(typeof(ArgumentException)); new PopupMenu(0, DialogBehavior.Default); } public void AnEnumWithoutFlags_Throws() { CheckThrow(typeof(ArgumentException)); new PopupMenu(NotFlags.Only, DialogBehavior.Default); } public void NullBehavior_Throws() { CheckThrow(typeof(ArgumentNullException)); new PopupMenu(TestChoice.First, null); } public void TheBehaviorsOutsidePressSetting_IsTaken() { var dismissing = new DialogBehavior( DialogBehaviorTests.Pressing(KeyCode.MainEnter, KeyCode.NumpadEnter), DialogBehaviorTests.Pressing(KeyCode.Escape), dismissOnOutsidePress: true); CheckTrue(new PopupMenu(TestChoice.First, dismissing).DismissOnOutsidePress); CheckFalse(new PopupMenu(TestChoice.First, DialogBehavior.Default).DismissOnOutsidePress); } } public class MaterialTests : TestFixture { public void Setting_GivesEachItemTheLookAtItsBit() { var looks = new[] { DropdownTests.Arrow(1, 1), DropdownTests.Arrow(2, 2), DropdownTests.Arrow(3, 3) }; var menu = new PopupMenu(TestChoice.First | TestChoice.Third, DialogBehavior.Default); menu.PopupMenuMaterial = Measurements with { Items = looks }; CheckEqual(looks[0], menu.ItemFor(TestChoice.First).ButtonMaterial); CheckEqual(looks[2], menu.ItemFor(TestChoice.Third).ButtonMaterial); } public void ALookTheArrayDoesNotReach_LeavesTheItemUnstyled() { var menu = new PopupMenu(TestChoice.First | TestChoice.Second, DialogBehavior.Default); menu.PopupMenuMaterial = Measurements with { Items = new[] { DropdownTests.Arrow(1, 1) } }; CheckEqual(default(ButtonMaterial), menu.ItemFor(TestChoice.Second).ButtonMaterial); } public void NoItemLooks_LeavesEveryItemUnstyled() { PopupMenu menu = NewMenu(material: new PopupMenuMaterial { Background = null, Spacing = 4, }); CheckEqual(default(ButtonMaterial), menu.ItemFor(TestChoice.First).ButtonMaterial); } public void Getting_ReturnsWhatWasSet() { PopupMenu menu = NewMenu(); CheckEqual(Measurements.Spacing, menu.PopupMenuMaterial.Spacing); CheckEqual(Measurements.Items, menu.PopupMenuMaterial.Items); } public void ABackground_FramesTheMenu() { Material background = DropdownTests.Bordered3; PopupMenu menu = NewMenu(material: Measurements with { Background = background }); CheckEqual(background, menu.Material); CheckEqual(new Vector(64, 82), menu.Bounds.Size); } public void WithItemOnTheMaterial_PlacesTheLookAtTheChoicesBit() { ButtonMaterial look = DropdownTests.Arrow(5, 5); PopupMenuMaterial material = new PopupMenuMaterial { Background = null, }.WithItem(TestChoice.Third, look); CheckEqual(3, material.Items.Length); CheckEqual(look, material.Items[2]); } public void WithItemOnTheMaterial_KeepsTheLooksAlreadyThere() { ButtonMaterial first = DropdownTests.Arrow(1, 1); ButtonMaterial third = DropdownTests.Arrow(3, 3); PopupMenuMaterial material = new PopupMenuMaterial { Background = null } .WithItem(TestChoice.Third, third) .WithItem(TestChoice.First, first); CheckEqual(3, material.Items.Length); CheckEqual(first, material.Items[0]); CheckEqual(third, material.Items[2]); } public void WithItemOnTheMaterial_LeavesTheOriginalAlone() { PopupMenuMaterial original = Measurements; PopupMenuMaterial changed = original.WithItem(TestChoice.First, DropdownTests.Arrow(9, 9)); CheckFalse(ReferenceEquals(original.Items, changed.Items)); CheckFalse(changed.Items[0].Equals(original.Items[0])); } } public class LayoutTests : TestFixture { public void TheItems_StackTopToBottomByBit() { PopupMenu menu = NewMenu(); CheckEqual(Box(4, 4, 50, 20), menu.ItemFor(TestChoice.First).Bounds); CheckEqual(Box(4, 28, 50, 20), menu.ItemFor(TestChoice.Second).Bounds); CheckEqual(Box(4, 52, 50, 20), menu.ItemFor(TestChoice.Third).Bounds); } public void TheMenu_FitsItsItemsAndTheSpacing() { CheckEqual(new Vector(58, 76), NewMenu().Bounds.Size); } public void EveryItem_IsAsWideAsTheWidestItem() { PopupMenu menu = NewMenu(material: Measurements with { Items = new[] { ItemOf(46, 16), ItemOf(86, 16), ItemOf(46, 16) }, }); CheckEqual(90, menu.ItemFor(TestChoice.First).Bounds.Width); CheckEqual(90, menu.ItemFor(TestChoice.Third).Bounds.Width); CheckEqual(98, menu.Bounds.Width); } public void ItemsOfDifferentHeights_KeepTheirOwn() { PopupMenu menu = NewMenu(material: Measurements with { Items = new[] { ItemOf(46, 16), ItemOf(46, 36), ItemOf(46, 16) }, }); CheckEqual(Box(4, 28, 50, 40), menu.ItemFor(TestChoice.Second).Bounds); CheckEqual(Box(4, 72, 50, 20), menu.ItemFor(TestChoice.Third).Bounds); CheckEqual(96, menu.Bounds.Height); } public void ChangingTheMaterial_ResizesTheMenuKeepingItsPosition() { PopupMenu menu = OpenedAt(new Vector(100, 100)); menu.PopupMenuMaterial = Measurements with { Spacing = 10 }; CheckEqual(new Vector(100, 100), menu.Bounds.Position); CheckEqual(new Vector(70, 100), menu.Bounds.Size); } } public class OpenAtTests : TestFixture { public void Always_IsOpenAtOnce() { PopupMenu menu = NewMenu(); menu.OpenAt(new Vector(100, 100)); CheckTrue(menu.IsOpen); } public void AfterAnUpdate_PutsTheTopLeftCornerAtThePosition() { PopupMenu menu = OpenedAt(new Vector(300, 200)); CheckTrue(menu.Visible); CheckEqual(Box(300, 200, 58, 76), menu.Bounds); } public void NearTheRightEdge_SlidesLeftToStayOnScreen() { CheckEqual(Box(742, 200, 58, 76), OpenedAt(new Vector(790, 200)).Bounds); } public void NearTheBottomEdge_SlidesUpToStayOnScreen() { CheckEqual(Box(300, 524, 58, 76), OpenedAt(new Vector(300, 590)).Bounds); } public void OpeningAgainElsewhere_MovesIt() { PopupMenu menu = OpenedAt(new Vector(300, 200)); menu.Close(); menu.OpenAt(new Vector(50, 60)); PopupTests.Tick(menu, Box(0, 0, 10, 10)); CheckEqual(Box(50, 60, 58, 76), menu.Bounds); } public void TheFirstEnabledItem_StartsWithFocus() { PopupMenu menu = NewMenu(); menu.OpenAt(new Vector(10, 10)); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.First), menu.FocusScope.InitialFocus)); } public void WithTheFirstItemDisabled_FocusStartsOnTheNext() { PopupMenu menu = NewMenu(); menu.ItemFor(TestChoice.First).Enabled = false; menu.OpenAt(new Vector(10, 10)); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Second), menu.FocusScope.InitialFocus)); } public void WithEveryItemDisabled_FocusStartsNowhere() { PopupMenu menu = NewMenu(); menu.ItemFor(TestChoice.First).Enabled = false; menu.ItemFor(TestChoice.Second).Enabled = false; menu.ItemFor(TestChoice.Third).Enabled = false; menu.OpenAt(new Vector(10, 10)); CheckNull(menu.FocusScope.InitialFocus); } public void OpeningAfterTheFirstItemWasEnabledAgain_FocusesItAgain() { PopupMenu menu = NewMenu(); menu.ItemFor(TestChoice.First).Enabled = false; menu.OpenAt(new Vector(10, 10)); menu.Close(); menu.ItemFor(TestChoice.First).Enabled = true; menu.OpenAt(new Vector(10, 10)); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.First), menu.FocusScope.InitialFocus)); } } public class ChoosingTests : TestFixture { public void ActivatingAnItem_RaisesItsChoiceAndClosesTheMenu() { PopupMenu menu = OpenedAt(new Vector(10, 10)); var chosen = new List(); menu.OnChosen += (choice, keys) => chosen.Add(choice); Click(menu.ItemFor(TestChoice.Second)); CheckEqual(new List { TestChoice.Second }, chosen); CheckFalse(menu.IsOpen); CheckFalse(menu.Visible); } public void TheHandler_IsGivenTheManagerThatActivatedTheItem() { PopupMenu menu = OpenedAt(new Vector(10, 10)); KeyboardManager given = null; var activating = new KeyboardManager(); menu.OnChosen += (choice, keys) => given = keys; menu.ItemFor(TestChoice.First).Click( default, MouseButtonFlags.Left, 1, NoKeys.Held, activating); CheckTrue(ReferenceEquals(activating, given)); } public void TheMenu_IsStillOpenWhileTheHandlerRuns() { PopupMenu menu = OpenedAt(new Vector(10, 10)); bool wasOpen = false; menu.OnChosen += (choice, keys) => wasOpen = menu.IsOpen; Click(menu.ItemFor(TestChoice.First)); CheckTrue(wasOpen); } public void ADisabledItem_ChoosesNothing() { PopupMenu menu = OpenedAt(new Vector(10, 10)); menu.ItemFor(TestChoice.Second).Enabled = false; int chosen = 0; menu.OnChosen += (choice, keys) => ++chosen; Click(menu.ItemFor(TestChoice.Second)); CheckEqual(0, chosen); CheckTrue(menu.IsOpen); } public void NoHandler_StillClosesTheMenu() { PopupMenu menu = OpenedAt(new Vector(10, 10)); Click(menu.ItemFor(TestChoice.Third)); CheckFalse(menu.IsOpen); } } public class KeyTests : TestFixture { [IgnoreTest] private static FocusedKeys FocusOn(PopupMenu menu, TestChoice choice) { return new FocusedKeys(menu, menu.ItemFor(choice)); } public void Escape_ClosesTheMenuChoosingNothing() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); int chosen = 0; menu.OnChosen += (choice, manager) => ++chosen; CheckTrue(keys.Press(KeyCode.Escape)); CheckFalse(menu.IsOpen); CheckEqual(0, chosen); } public void Enter_ChoosesTheFocusedItem() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.Second); var chosen = new List(); menu.OnChosen += (choice, manager) => chosen.Add(choice); keys.Press(KeyCode.MainEnter); keys.Release(KeyCode.MainEnter); CheckEqual(new List { TestChoice.Second }, chosen); } public void ArrowDown_MovesFocusToTheNextItem() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); CheckTrue(keys.Press(KeyCode.ArrowDown)); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Second), keys.Manager.Focused)); } public void ArrowUp_MovesFocusToThePreviousItem() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.Third); CheckTrue(keys.Press(KeyCode.ArrowUp)); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Second), keys.Manager.Focused)); } public void ArrowDownFromTheLastItem_WrapsToTheFirst() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.Third); keys.Press(KeyCode.ArrowDown); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.First), keys.Manager.Focused)); } public void ArrowUpFromTheFirstItem_WrapsToTheLast() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); keys.Press(KeyCode.ArrowUp); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Third), keys.Manager.Focused)); } public void ArrowDown_SkipsADisabledItem() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); menu.ItemFor(TestChoice.Second).Enabled = false; keys.Press(KeyCode.ArrowDown); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Third), keys.Manager.Focused)); } public void ArrowDown_WithTheOnlyEnabledItemFocused_StaysThere() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); menu.ItemFor(TestChoice.Second).Enabled = false; menu.ItemFor(TestChoice.Third).Enabled = false; keys.Press(KeyCode.ArrowDown); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.First), keys.Manager.Focused)); } public void AnotherKey_IsLeftAlone() { PopupMenu menu = OpenedAt(new Vector(10, 10)); FocusedKeys keys = FocusOn(menu, TestChoice.First); keys.Press(KeyCode.Q); CheckTrue(menu.IsOpen); } public void ABehaviorWithNoKeys_StillMovesWithTheArrows() { var menu = new PopupMenu(TestChoice.First | TestChoice.Second, DialogBehavior.NoKeys); menu.PopupMenuMaterial = Measurements; menu.OpenAt(new Vector(10, 10)); PopupTests.Tick(menu, Box(0, 0, 10, 10)); var keys = new FocusedKeys(menu, menu.ItemFor(TestChoice.First)); keys.Press(KeyCode.ArrowDown); keys.Manager.Refresh(); CheckTrue(ReferenceEquals(menu.ItemFor(TestChoice.Second), keys.Manager.Focused)); } public void ABehaviorWithNoKeys_IgnoresEscape() { var menu = new PopupMenu(TestChoice.First, DialogBehavior.NoKeys); menu.PopupMenuMaterial = Measurements; menu.OpenAt(new Vector(10, 10)); PopupTests.Tick(menu, Box(0, 0, 10, 10)); var keys = new FocusedKeys(menu, menu.ItemFor(TestChoice.First)); keys.Press(KeyCode.Escape); CheckTrue(menu.IsOpen); } } public class MouseTests : TestFixture { public void APressOutside_ClosesItWhenDismissingIsOn() { PopupMenu menu = OpenedAt(new Vector(10, 10)); menu.DismissOnOutsidePress = true; int chosen = 0; menu.OnChosen += (choice, keys) => ++chosen; menu.MouseDown(PopupTests.At(PopupTests.OutsideThePopup), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckFalse(menu.IsOpen); CheckEqual(0, chosen); } public void APressOutside_LeavesItOpenWhenDismissingIsOff() { PopupMenu menu = OpenedAt(new Vector(10, 10)); menu.MouseDown(PopupTests.At(PopupTests.OutsideThePopup), MouseButtonFlags.Left, NoKeys.Held, NoKeys.Manager); CheckTrue(menu.IsOpen); } } } }