using Annulus; using Azimuth; using System; namespace Glass.Testbed { /// /// One named UI experiment. /// public abstract class TestCase { // Margin separates a thing from its neighbour; Gutter separates one band of the layout from the next. public const int Margin = 8; public const int Gutter = 12; public string Name { get; } public Panel Root { get; } protected TestCase(string name) { Name = name; // Invisible rather than null: a null material reaches NullRenderer, which BlacklightRenderer throws on. Root = new Panel { Material = Material.Invisible, DebugName = name }; } public Rect ClientRect => Rect.FromPositionAndSize(Vector.Zero, Root.ClientRect.Size); protected RectAggregator ContentAggregator; public Rect ContentBounds => ContentAggregator.Rect; public void Add(Control control) { Root.ChildControls.Add(control); } public T NewChild(string debugName = null) where T : Control, new() { return Root.ChildControls.NewChild(debugName); } private void PlaceCaption(TextMaterial material, Rect row) { Panel caption = material.BuildControl(Constraints.Unbounded); caption.Bounds = row; Add(caption); } /// /// Adds a row to this case's content sized to the caption's own text, and puts the caption in it. /// protected void AddCaption(StringView text) { TextMaterial material = Widgets.Caption(text); PlaceCaption(material, ContentAggregator.NewRow(material, margin: 4)); } /// /// Takes the row off instead, for a caption inside a rect the case has already /// claimed rather than one at the end of its content. /// protected void AddCaption(StringView text, ref Rect remaining) { TextMaterial material = Widgets.Caption(text); PlaceCaption(material, remaining.NewRow(material, margin: 4)); } /// /// Lays this case out within . Runs once. /// /// /// Not the constructor, because the area isn't known until the tab strip has been built and measured, /// and the tab strip is built from the cases. /// public void Build(Rect area) { Root.Bounds = area; // Seeded with the client width so a full-width row still spans the window, but with no height, so a case // is free to grow past the bottom of it. ContentAggregator = new RectAggregator( Rect.FromPositionAndSize(Vector.Zero, new Vector(ClientRect.Width, 0)), new Vector(Gutter, Gutter)); Populate(); Root.Bounds = Rect.FromPositionAndSize(area.TopLeft, ContentBounds.Size); } /// /// Fills this case with whatever it is demonstrating. /// protected abstract void Populate(); /// /// Runs each time this case is shown, so one a user can leave in some other state resets itself. /// protected virtual void Setup() { } /// /// Runs each tick this case is the shown one, for a case with something moving in it. /// public virtual void Update(TimeSpan elapsed) { } public void Show(ControlContainer host) { host.Add(Root); Setup(); } public void Hide(ControlContainer host) { host.Remove(Root); } } }