using Annulus; using Azimuth; using System; namespace Glass.Testbed { /// /// Dropdowns opening beside their faces: below when there's room, above when there isn't, scrolling past their /// height cap, and out of the clip of a small parent. Picking an item shows it on the face and closes the list. /// public class DropdownTestCase : TestCase { private static readonly string[] Fruit = { "Apple", "Banana", "Cherry", "Damson", "Elderberry", "Fig", "Grape", "Honeydew", "Kiwi", "Lemon", "Mango", "Nectarine", "Orange", "Papaya", "Quince", "Raspberry", "Satsuma", "Tangerine", }; // The packsheet's arrows look the same pressed as not, so this one tints per state to show the dropdown is // out. private static readonly ButtonMaterial ArrowButtonMaterial = new ButtonMaterial(UiPacksheetMaterials.NubDown with { Scale = 2 }) { HoverMaterial = UiPacksheetMaterials.NubDown with { Scale = 2, TintColor = System.Drawing.Color.LightSkyBlue, }, PressedMaterial = UiPacksheetMaterials.NubDown with { Scale = 2, TintColor = System.Drawing.Color.DodgerBlue, }, HoverPressedMaterial = UiPacksheetMaterials.NubDown with { Scale = 2, TintColor = System.Drawing.Color.DodgerBlue, }, DisabledMaterial = UiPacksheetMaterials.NubDown with { Scale = 2, TintColor = System.Drawing.Color.Gray }, }; private static readonly DropdownMaterial FaceMaterial = new DropdownMaterial { Background = UiPacksheetMaterials.TextField, ArrowButton = ArrowButtonMaterial, ArrowGap = Margin, Popup = Widgets.PopupList, }; public DropdownTestCase(string name) : base(name) { } private static Vector WidestLabelSize(string[] choices) { Vector returnMe = Vector.Zero; foreach (string choice in choices) { returnMe = Vector.Max(returnMe, Widgets.ButtonLabel(choice).PreferredSize(Constraints.Unbounded)); } return returnMe; } /// /// A dropdown of the first fruit, sized to its widest, whose list scrolls /// past . /// private static Dropdown BuildDropdown(string debugName, int choiceCount, Scalar maxPopupHeight, Anchor arrowAnchor = Anchor.Right) { string[] choices = new string[choiceCount]; Array.Copy(Fruit, choices, choiceCount); var dropdown = new Dropdown { DebugName = debugName, DropdownMaterial = FaceMaterial, ArrowAnchor = arrowAnchor, }; dropdown.Bounds = Rect.FromPositionAndSize( Vector.Zero, FaceMaterial.BoundsSizeFor(WidestLabelSize(choices))); dropdown.Popup.MaxHeight = maxPopupHeight; dropdown.Rebuild(choices, choice => Widgets.ButtonLabel(choice)); dropdown.Select(0); return dropdown; } private static void PlaceInRow(ref Rect row, Control control) { Rect slot = row.NewCol(control.Bounds.Width, margin: Gutter); control.Bounds = control.Bounds.WithPosition(slot.TopLeft); } protected override void Populate() { AddCaption("Click a face or its arrow to open its list; click an item to pick it, or anywhere else to"); AddCaption("dismiss it. A list opens below its face when it fits, above when it doesn't, and scrolls"); AddCaption("once it's taller than its cap. The arrow can sit on either side."); AddCaption("Right: a dropdown in a small parent, whose clip it escapes."); Scalar uncapped = Scalar.PositiveInfinity; Dropdown few = BuildDropdown("five choices", choiceCount: 5, maxPopupHeight: uncapped); Dropdown many = BuildDropdown("capped at 200", choiceCount: 18, maxPopupHeight: 200); Dropdown arrowOnLeft = BuildDropdown( "arrow on the left", choiceCount: 6, maxPopupHeight: uncapped, arrowAnchor: Anchor.Left); Dropdown clipped = BuildDropdown("in a small parent", choiceCount: 8, maxPopupHeight: uncapped); var parent = new Panel { DebugName = "small parent", Material = UiPacksheetMaterials.GreenFlatPanel }; parent.Bounds = Rect.FromPositionAndSize( Vector.Zero, UiPacksheetMaterials.GreenFlatPanel.BoundsSizeFor(clipped.Bounds.Size)); parent.CanvasChildren.Add(clipped); Rect topRow = ContentAggregator.NewRow(Math.Max(few.Bounds.Height, parent.Bounds.Height)); PlaceInRow(ref topRow, few); PlaceInRow(ref topRow, many); PlaceInRow(ref topRow, arrowOnLeft); PlaceInRow(ref topRow, parent); Add(few); Add(many); Add(arrowOnLeft); Add(parent); AddCaption("At the bottom of the window, so its list opens above it"); Dropdown bottom = BuildDropdown("at the bottom", choiceCount: 8, maxPopupHeight: uncapped); Rect area = ContentRect; Rect bottomSlot = area.NewRow(bottom.Bounds.Height, VerticalJustification.Bottom); // Placed against the area's bottom rather than after the rows above, so ContentAggregator is stretched to // reach it, or Root.Bounds would clip it. Scalar reserve = bottomSlot.Bottom - ContentBounds.Height; if (reserve > 0) { ContentAggregator.NewRow(reserve); } bottom.Bounds = bottom.Bounds.WithPosition(bottomSlot.TopLeft); Add(bottom); } } }