using Annulus; using Azimuth; using System; using System.Collections.Generic; using System.Drawing; namespace Glass.Testbed { /// /// ProgressBars filling from each edge, driven together by a NumberField, each with a label split across the edge /// of the fill. A slow sweep shows how a fill edge that falls between pixels looks. /// public class ProgressBarTestCase : TestCase { private static readonly Color DarkText = Color.FromArgb(24, 28, 34); private static readonly ProgressBarMaterial BarMaterial = new ProgressBarMaterial { Background = new SolidColorMaterial { Color = DarkText, BorderSize = new Rect.BorderSize { Top = 2, Bottom = 2, Left = 2, Right = 2 }, }, Empty = new SolidColorMaterial { Color = Color.FromArgb(190, 196, 206) }, Fill = new SolidColorMaterial { Color = Color.FromArgb(40, 110, 200) }, }; private static readonly Material GoalMarker = new SolidColorMaterial { Color = Color.Orange }; private static readonly NumberFieldMaterial FieldMaterial = new NumberFieldMaterial { Background = UiPacksheetMaterials.TextField, Label = Widgets.FieldText("") with { VerticalAlignment = Alignment.Vertical.Center }, IncrementButton = UiPacksheetMaterials.ScrollBarUpArrow, DecrementButton = UiPacksheetMaterials.ScrollBarDownArrow, }; private static readonly Vector HorizontalSize = new Vector(320, 32); private static readonly Vector VerticalSize = new Vector(72, 200); // Slow enough that the sweep's fill edge sits between pixels on most frames. private const double SweepPerSecond = 0.02; private readonly List<(ProgressBar Bar, TextMaterial EmptyLabel, TextMaterial FilledLabel)> drivenBars = new List<(ProgressBar Bar, TextMaterial EmptyLabel, TextMaterial FilledLabel)>(); private NumberField field; private ProgressBar sweepBar; private double sweepProgress; public ProgressBarTestCase(string name) : base(name) { } private static TextMaterial Label(StringView text, Color color) { return Widgets.SmallCaption(text) with { TintColor = color, HorizontalAlignment = Alignment.Horizontal.Center, VerticalAlignment = Alignment.Vertical.Center, }; } /// /// A bar with the same label in both parts: dark over the empty part, light over the fill. /// private static (ProgressBar Bar, TextMaterial EmptyLabel, TextMaterial FilledLabel) BuildBar( string debugName, Anchor fillFrom, Rect bounds, StringView text) { var bar = new ProgressBar { DebugName = debugName, ProgressBarMaterial = BarMaterial, FillFrom = fillFrom, Bounds = bounds, }; TextMaterial emptyLabel = Label(text, DarkText); TextMaterial filledLabel = Label(text, Color.White); bar.EmptyContent.CanvasChildren.Add( new Panel { DebugName = "empty label", Material = emptyLabel, Bounds = bar.CanvasRect }); bar.FilledContent.CanvasChildren.Add( new Panel { DebugName = "filled label", Material = filledLabel, Bounds = bar.CanvasRect }); return (bar, emptyLabel, filledLabel); } private void AddDrivenBar(string debugName, Anchor fillFrom, Rect bounds) { var driven = BuildBar(debugName, fillFrom, bounds, ""); drivenBars.Add(driven); Add(driven.Bar); } private void ApplyFieldValue() { Scalar progress = (double)(field.Value / 100); foreach (var driven in drivenBars) { driven.Bar.Progress = progress; driven.EmptyLabel.Text = field.DisplayText; driven.FilledLabel.Text = field.DisplayText; } } protected override void Populate() { AddCaption("Step or scrub the number to drive the bars."); AddCaption("Each label is two copies, one clipped to each part,"); AddCaption("so it changes colour where the fill ends."); AddCaption("The orange mark is on the top bar itself, over both parts."); field = new NumberField(new NumberFieldBehavior(), new NumberField.Data { Grid = new NumberGrid { Max = 100, Resolution = 0.1m, Step = 1 }, Value = 40, Format = (value, culture) => value.ToString("F1", culture) + "%", }) { DebugName = "progress", NumberFieldMaterial = FieldMaterial, }; Vector fieldSize = field.PreferredBoundsSize; Rect fieldRow = ContentAggregator.NewRow(fieldSize.Y); field.Bounds = fieldRow.NewCol(fieldSize.X); Add(field); Rect leftRow = ContentAggregator.NewRow(HorizontalSize.Y); AddDrivenBar("from left", Anchor.Left, leftRow.NewCol(HorizontalSize.X)); Rect rightRow = ContentAggregator.NewRow(HorizontalSize.Y); AddDrivenBar("from right", Anchor.Right, rightRow.NewCol(HorizontalSize.X)); Rect verticalRow = ContentAggregator.NewRow(VerticalSize.Y); AddDrivenBar("from top", Anchor.Top, verticalRow.NewCol(VerticalSize.X, margin: Gutter)); AddDrivenBar("from bottom", Anchor.Bottom, verticalRow.NewCol(VerticalSize.X)); ProgressBar markedBar = drivenBars[0].Bar; Rect track = markedBar.CanvasRect; markedBar.CanvasChildren.Add(new Panel { DebugName = "goal marker", Material = GoalMarker, Bounds = Rect.FromPositionAndSize(new Vector(track.Width * 0.75 - 1, 0), new Vector(2, track.Height)), }); field.OnValueChanged += (_, __, ___) => ApplyFieldValue(); ApplyFieldValue(); AddCaption("A slow sweep, for a fill edge that falls between pixels"); Rect sweepRow = ContentAggregator.NewRow(HorizontalSize.Y); sweepBar = BuildBar("sweep", Anchor.Left, sweepRow.NewCol(HorizontalSize.X), "Sub-pixel sweep").Bar; Add(sweepBar); } protected override void Setup() { sweepProgress = 0; } public override void Update(TimeSpan elapsed) { sweepProgress = (sweepProgress + elapsed.TotalSeconds * SweepPerSecond) % 1; sweepBar.Progress = sweepProgress; } } }