Skip to content
Shiny.NET

Gamepad — Layouts & Customizing

A layout is a GamepadLayout holding a list of GamepadElements. Every GamepadLayouts.* call returns a fresh copy that is safe to change, so the quickest custom layout is a tweaked preset:

var layout = GamepadLayouts.Standard();
layout.Find("ls")!.IsFloating = true; // left stick follows the thumb
layout.Find("a")!.IsTurbo = true; // rapid-fire A
layout.Find("a")!.Label = "Jump";
Pad.ControllerLayout = layout;

Or build one from scratch:

var layout = new GamepadLayout
{
Name = "Racer",
FaceStyle = GamepadFaceStyle.Xbox,
Elements =
[
new() { Id = "steer", Kind = GamepadElementKind.Stick, Button = GamepadButton.LeftStick,
Anchor = GamepadAnchor.BottomLeft, X = 110, Y = -110, Width = 150, Height = 150 },
new() { Id = "gas", Kind = GamepadElementKind.Trigger, Button = GamepadButton.RightTrigger,
Shape = GamepadElementShape.Rounded, Anchor = GamepadAnchor.BottomRight,
X = -80, Y = -120, Width = 90, Height = 140, Label = "GAS" },
new() { Id = "brake", Kind = GamepadElementKind.Trigger, Button = GamepadButton.LeftTrigger,
Shape = GamepadElementShape.Rounded, Anchor = GamepadAnchor.BottomRight,
X = -190, Y = -100, Width = 90, Height = 100, Label = "BRAKE", Color = "#B91C1C" }
]
};
GamepadElement Meaning
Id Unique within the layout. Find(id) looks it up.
Kind Button, Trigger (also drives its trigger axis), DPad or Stick
Button What a button reports. For a stick, LeftStick or RightStick says which stick it is.
Shape Circle, Pill or Rounded — buttons and triggers only
Anchor, X, Y The element’s centre, offset from one of nine anchor points in device-independent units (positive Y is down)
Width, Height Size in device-independent units
Label, Color Override the face style’s label and fill (#RRGGBB)
IsFloating, FloatingZone Floating stick and how far around it a touch grabs it
IsTurbo Rapid-fire while held

DesignWidth / DesignHeight are the canvas the layout was designed on. Only Uniform sizing uses them. BodyRoundness shapes the body drawn behind it — 0.5 is the SNES “dog bone”.

Set IsEditing and the controller stops playing. The player drags an element to move it and pinches with a second finger to resize it. When a move finishes, LayoutEdited fires and ControllerLayout holds the edited layout. The MAUI property is two-way by default, and Blazor supports @bind-ControllerLayout.

Persist it as JSON:

Pad.LayoutEdited += (_, layout) => Preferences.Set("pad", layout.ToJson());
var saved = Preferences.Get("pad", null);
if (saved != null)
Pad.ControllerLayout = GamepadLayout.FromJson(saved);

The JSON writes enums as names, so a saved layout survives a library update that reorders them. Reset by setting ControllerLayout back to null, which returns to Preset.

MAUI (Color) Blazor (CSS string) CSS variable Paints
ButtonColor ButtonColor --shiny-gamepad-button Unpressed elements
PressedColor PressedColor --shiny-gamepad-pressed Pressed d-pad arms, stick knobs
LabelColor LabelColor --shiny-gamepad-label Labels and glyphs the face style leaves uncoloured
OutlineColor OutlineColor --shiny-gamepad-outline Element outlines
BodyColor BodyColor --shiny-gamepad-body The controller body in Uniform sizing
AccentColor AccentColor --shiny-gamepad-accent Edit-mode outlines, a clicked stick’s ring

On Blazor you can also set the CSS variables from a stylesheet. Parameters that are left null fall back to them.

Property Default
ControllerScale 1 Size multiplier in Anchored sizing
DPadMode EightWay FourWay never reports a diagonal
TurboRate 10 Presses per second for turbo buttons
StickClickEnabled true Double-tap-and-hold clicks a stick
PassThrough true Touches that miss every element reach the view beneath
ShowBody null Draw the body — null draws it only in Uniform sizing
PlayerIndex null The player slot Gamepad reports
GamepadId generated Stable IGamepad.Id, kept across the pads a view hands out
GamepadName “On-Screen Gamepad” IGamepad.Name