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