using Annulus; using Azimuth; namespace Glass.Testbed { /// /// A ScrollingPanel: children laid out in a content region taller than the panel, with the panel showing a /// scrolled-and-clipped window onto it. The buttons drive it through ScrollBy, and the mouse wheel through its /// StepAmount. /// public class ScrollingPanelTestCase : TestCase { private static readonly Material[] RowPalette = { UiPacksheetMaterials.RedFlatPanel, UiPacksheetMaterials.YellowFlatPanel, UiPacksheetMaterials.BlueFlatPanel, }; public ScrollingPanelTestCase(string name) : base(name) { } /// /// The height that fits a row label inside every palette material, so the rows stay uniform whichever /// material each one wears. /// private static Scalar RowHeightFor(Scalar width) { TextMaterial label = Widgets.ButtonLabel("Row 0"); Constraints offer = Constraints.TightWidth(width, Scalar.PositiveInfinity); Vector returnMe = Vector.Zero; foreach (Material rowMaterial in RowPalette) { returnMe = Vector.Max(returnMe, Material.SolveBoundsFor(rowMaterial, label, offer)); } return returnMe.Y; } private static Button BuildScrollButton( StringView caption, Vector labelSize, ScrollingPanel scroller, Scalar deltaY) { Button button = Widgets.ButtonLabel(caption) .BuildControl(Constraints.Tight(labelSize)) .WrapIn(UiPacksheetMaterials.YellowButton); button.OnClick += delegate (in MouseState _, MouseButtonFlags __) { scroller.ScrollBy(new Vector(0, deltaY)); }; return button; } // The row is consumed a column at a time, so it has to be the caller's rect rather than a copy of it. private void AddToRow(ref Rect row, Button button) { Rect slot = row.NewCol(button.Bounds.Width, margin: Margin); button.Bounds = button.Bounds.WithPosition(slot.TopLeft); Add(button); } protected override void Populate() { var viewportSize = new Vector(240, 190); const int rowCount = 16; AddCaption("A list too tall for its panel — the panel clips and scrolls a window onto it"); AddCaption("Scroll up / down shift the content; rows past the edge are clipped, not laid out short"); AddCaption("The mouse wheel scrolls the panel too, a quarter of its height per notch"); Rect stage = ContentAggregator.NewRow(viewportSize.Y); var scroller = NewChild("scroller"); scroller.Material = UiPacksheetMaterials.GreenFlatPanel; scroller.Bounds = stage.NewCol(viewportSize.X); Scalar rowHeight = RowHeightFor(scroller.ViewportRect.Width); var content = new RectAggregator( Rect.FromPositionAndSize(Vector.Zero, new Vector(scroller.ViewportRect.Width, 0)), new Vector(0, Margin)); for (int index = 0; index < rowCount; ++index) { Rect rowSlot = content.NewRow(rowHeight); var row = scroller.CanvasChildren.NewChild($"row {index}"); row.Material = RowPalette[index % RowPalette.Length]; row.Bounds = rowSlot; Panel label = Widgets.ButtonLabel($"Row {index}").BuildControl(Constraints.Unbounded); label.Bounds = Rect.FromPositionAndSize( Control.CenteredIn(row.CanvasRect.Size, label.Bounds.Size), label.Bounds.Size); row.CanvasChildren.Add(label); } scroller.ContentSize = content.Rect.Size; // Both buttons share the wider label's size, so the pair reads as a set. Vector labelSize = Vector.Max( Widgets.ButtonLabel("Scroll up").PreferredSize(Constraints.Unbounded), Widgets.ButtonLabel("Scroll down").PreferredSize(Constraints.Unbounded)); Scalar step = (rowHeight + Margin) * 3; Button scrollUp = BuildScrollButton("Scroll up", labelSize, scroller, -step); Button scrollDown = BuildScrollButton("Scroll down", labelSize, scroller, step); Rect buttonRow = ContentAggregator.NewRow(scrollUp.Bounds.Height); AddToRow(ref buttonRow, scrollUp); AddToRow(ref buttonRow, scrollDown); } } }