using Amphora;
using Annulus;
using Azimuth;
using Clave;
using System;
using System.Collections.Generic;
using Prism;
namespace Glass.Testbed.TestCases.Rendering
{
///
/// A guided tour whose spotlight the user can move or dismiss, so it needs a Setup to put itself back.
///
public class SpotlightTestCase : TestCase
{
///
/// Sits below whatever it is a child of, which under a Spotlight is the highlighted control.
///
private class CoachMark : Panel
{
public Vector Offset { get; set; }
public override void Update(Duration elapsed, UpdateState state)
{
base.Update(elapsed, state);
Bounds = Rect.FromPositionAndSize(
new Vector(0, state.ParentCanvasRect.Height) + Offset, Bounds.Size);
}
}
private static readonly Vector ButtonSize = MeasuredButtonSize();
private static Vector MeasuredButtonSize()
{
return UiPacksheetMaterials.Button.SolveBoundsFor(
Widgets.ButtonLabel("Build: 0"), Constraints.TightWidth(190, Scalar.PositiveInfinity));
}
private readonly List targets = new List();
private readonly Spotlight spotlight;
private readonly Label coachLabel;
private int step;
public SpotlightTestCase()
{
spotlight = new Spotlight(new SolidColorMaterial
{
Color = Color.FromArgb(170, 0, 0, 0),
})
{
DebugName = "spotlight",
};
coachLabel = new Label(Widgets.ButtonLabelStyle, "Click");
}
///
/// Re-parenting is the whole move: nothing else tells the spotlight where its new target sits.
///
private void MoveSpotlightTo(int target)
{
targets[step].CanvasChildren.Remove(spotlight);
step = target;
targets[step].CanvasChildren.Add(spotlight);
coachLabel.Text = $"Click {targets[step].DebugName}";
}
private void BuildTargets()
{
Rect buttonRow = ContentAggregator.NewRow(ButtonSize.Y);
foreach (string name in new[] { "Build", "Attack", "Harvest" })
{
string buttonName = name;
int clicks = 0;
var label = new Label(Widgets.ButtonLabelStyle, $"{buttonName}: 0");
Button target = label.FillParent(new Button(ButtonBehavior.Default)
{
DebugName = buttonName,
ButtonMaterial = UiPacksheetMaterials.Button,
Bounds = buttonRow.NewCol(ButtonSize.X, margin: Gutter),
});
target.OnActivate += delegate (ButtonBase _, in KeyboardSnapshot __, KeyboardManager ___)
{
++clicks;
label.Text = $"{buttonName}: {clicks}";
};
Add(target);
targets.Add(target);
}
}
private static Button BuildCoachButton(string debugName, string text, Vector labelSize)
{
Button button = new Label(Widgets.ButtonLabelStyle, text, Constraints.Tight(labelSize))
.WrapIn(UiPacksheetMaterials.Button, ButtonBehavior.Default);
button.DebugName = debugName;
return button;
}
private void BuildCoachMark()
{
Scalar coachTextHeight = coachLabel.PreferredSize(Constraints.Unbounded).Y;
// Both buttons share the wider label's size so the row stays visually even.
Vector labelSize = Vector.Max(
Widgets.ButtonLabel("Next").PreferredSize(Constraints.Unbounded),
Widgets.ButtonLabel("Done").PreferredSize(Constraints.Unbounded));
Button next = BuildCoachButton("next step", "Next", labelSize);
Button done = BuildCoachButton("done", "Done", labelSize);
Vector buttonSize = next.Bounds.Size;
// Laid out before the mark exists, so the mark can be sized to what it ends up holding rather than to a
// guess that has to stay in step with it. The button row sets the width, and the label centres across it.
var content = new RectAggregator(
Rect.FromPositionAndSize(Vector.Zero, new Vector(2 * buttonSize.X + Gutter, 0)),
new Vector(0, Gutter));
coachLabel.Bounds = content.NewRow(coachTextHeight);
Rect coachButtonRow = content.NewRow(buttonSize.Y);
next.Bounds = coachButtonRow.NewCol(buttonSize.X, margin: Gutter);
done.Bounds = coachButtonRow.NewCol(buttonSize.X);
var coachMark = new CoachMark
{
DebugName = "coach mark",
Material = UiPacksheetMaterials.YellowPanel,
Bounds = Rect.FromPositionAndSize(
Vector.Zero, UiPacksheetMaterials.YellowPanel.BoundsSizeFor(content.Rect.Size)),
Offset = new Vector(0, Gutter),
};
spotlight.CanvasChildren.Add(coachMark);
coachMark.CanvasChildren.Add(coachLabel);
coachMark.CanvasChildren.Add(next);
coachMark.CanvasChildren.Add(done);
next.OnActivate += delegate (ButtonBase _, in KeyboardSnapshot __, KeyboardManager ___)
{
MoveSpotlightTo((step + 1) % targets.Count);
};
done.OnActivate += delegate (ButtonBase _, in KeyboardSnapshot __, KeyboardManager ___)
{
targets[step].CanvasChildren.Remove(spotlight);
};
}
protected override void Populate()
{
AddCaption("Everything dims but the highlighted button, which still takes its clicks");
AddCaption("Modal means the menu bar is blocked too — Done releases it");
BuildTargets();
BuildCoachMark();
}
///
/// Cases are built once, so a tour the user left part way through or dismissed restarts here.
///
protected override void Setup()
{
MoveSpotlightTo(0);
}
}
}