// UBER button — brushed steel bezel, orange glow ring, engraved caption.
// The metal face is an SkSL shader: angular streaks + two specular lobes, so it stays crisp at any size.
SkiaShape body = null, glow = null, face = null;
SkiaLabel caption = null;
// Band widths drive every size below: change one and the rest stay concentric.
const double size = 250, radius = 54; // the button itself
const double chrome = 9; // outer steel band
const double orange = 14; // ring band
const string brushed = """
uniform float4 iMouse;
uniform float iTime;
uniform float2 iResolution;
uniform float2 iImageResolution;
uniform shader iImage1;
uniform float2 iOffset;
half4 main(float2 fragCoord)
{
float2 uv = (fragCoord - iOffset) / iResolution.xy;
float2 p = uv - 0.5;
float a = atan(p.y, p.x);
float r = length(p) * 2.0;
// brushed grain: noise that follows the angle, so it reads as circular polishing
float grain = fract(sin(a * 210.0 + r * 8.0) * 43758.5453);
grain += fract(sin(a * 97.0 - r * 3.0) * 12345.6789) * 0.5;
grain = (grain / 1.5 - 0.5) * 0.16;
// two soft highlights opposite each other, like light across a turned surface
float sheen = 0.55 + 0.45 * cos(a * 2.0 + 0.6);
float base = mix(0.52, 0.92, sheen);
// darker towards the rim
base *= 1.0 - 0.18 * r * r;
float v = clamp(base + grain, 0.0, 1.0);
return half4(v * 0.96, v * 0.97, v, 1.0);
}
""";
// Engraved caption. The relief is SkiaLabel's own drop shadow (a light outline stroke offset
// down by whole pixels inside the paint), NOT stacked offset copies of the label: the paint
// offset is identical on every line and survives the press scale, stacked copies do not.
body = new SkiaShape
{
CornerRadius = radius,
WidthRequest = size,
HeightRequest = size,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Color.Parse("#B9BEC4"),
StrokeWidth = chrome > 0 ? 1 : 0, // the outer hairline belongs to the steel band, gone with it
StrokeColor = Color.Parse("#66FFFFFF"),
IsClippedToBounds = true, // the shader fills a rect, the clip gives it the rounded corners
Children = new List<SkiaControl>
{
// One piece of turned metal under the whole button, so the grain runs continuously
// from the outer band through to the face. The shader is a POST renderer - it paints
// over its control's content - so it gets its own layer and everything else is a sibling above.
new SkiaLayer
{
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
VisualEffects = new List<SkiaEffect>
{
new SkiaShaderEffect
{
ShaderCode = brushed,
UseBackground = PostRendererEffectUseBackgroud.Once,
AutoCreateInputTexture = true,
}
.OnShaderError((me, error) => Console.WriteLine("[SkSL] " + error)),
},
},
// Bezel lighting for the OUTER BAND ONLY: a stroke of width `chrome` drawn inward from the
// button edge, so the metal under the caption keeps the raw shader. A filled layer here
// would tint the whole face too.
new SkiaShape
{
CornerRadius = radius,
WidthRequest = size,
HeightRequest = size,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Colors.Transparent,
StrokeWidth = chrome,
Opacity = 0.5,
StrokeColor = Color.Parse("#858789"),
StrokeGradient = new SkiaGradient
{
Type = GradientType.Linear,
StartXRatio = 0.1f, StartYRatio = 0,
EndXRatio = 0.9f, EndYRatio = 1,
Colors = new List<Color>
{
Color.Parse("#ACACAC"), Color.Parse("#858789"),
Color.Parse("#5E6266"), Color.Parse("#919497"), Color.Parse("#54585C"),
},
ColorPositions = new List<double> { 0, 0.28, 0.52, 0.78, 1 },
},
},
// Orange ring drawn as a STROKE, not a filled plate: a fill would cover the metal in the
// middle, a stroke lets the shader through. SkiaShape strokes INWARD (the path is inset by
// half the stroke, SkiaShape.Shared.cs CalculateShapeSizeForStroke), so this control's size
// is the band's OUTER edge and the inner edge lands exactly on the face.
new SkiaShape
{
CornerRadius = radius - chrome,
WidthRequest = size - chrome * 2, // outer edge of the band
HeightRequest = size - chrome * 2,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
BackgroundColor = Colors.Transparent,
StrokeWidth = orange,
StrokeColor = Color.Parse("#FF8A00"),
StrokeGradient = new SkiaGradient
{
Type = GradientType.Linear,
StartXRatio = 0, StartYRatio = 0, EndXRatio = 0.4f, EndYRatio = 1,
Colors = new List<Color>
{
Color.Parse("#FFD08A"), Color.Parse("#FF9500"),
Color.Parse("#E96A00"), Color.Parse("#FFB347"),
},
},
}.Assign(out glow),
// face: a hole onto the same metal, only the caption and the inner edge belong to it
new SkiaShape
{
CornerRadius = radius - chrome - orange,
WidthRequest = size - (chrome + orange) * 2,
HeightRequest = size - (chrome + orange) * 2,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
StrokeWidth = 1,
StrokeColor = Color.Parse("#55FFFFFF"),
Children = new List<SkiaControl>
{
new SkiaLabel("CLICK\nHERE")
{
FontSize = 42,
FontWeight = 800,
FontFamily = "FontTextTitle",
TextColor = Color.Parse("#8D949C"),
DropShadowColor = Color.Parse("#F2F5F8"), // highlight below the letters = cut into the metal
DropShadowSize = 0.7,
DropShadowOffsetX = 0,
DropShadowOffsetY = 1.6,
HorizontalTextAlignment = DrawTextAlignment.Center,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
}.Assign(out caption),
}
}.Assign(out face),
}
};
body.WithGestures((me, args, apply) =>
{
// take the Down so nothing above steals the sequence, act on the release
if (args.Type == TouchActionResult.Down)
{
me.ScaleX = me.ScaleY = 0.97;
glow.Opacity = 0.75;
return me;
}
if (args.Type == TouchActionResult.Up)
{
me.ScaleX = me.ScaleY = 1;
glow.Opacity = 1;
// built-in shimmer, on the uncached body: a post-animator over a SkiaCacheType.Image control does not show
body.PlayShimmerAnimation(Color.Parse("#66FFFFFF"), shimmerWidth: 60, shimmerAngle: 20, speedMs: 600);
Console.WriteLine("clicked");
return me;
}
return null;
});
return body;