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);
}
}
}