mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-30 17:49:57 -06:00
245 lines
8.8 KiB
C#
245 lines
8.8 KiB
C#
using SkiaSharp;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using OriginalInputConfig = Ryujinx.Common.Configuration.Hid.InputConfig;
|
|
using OriginalPlayerIndex = Ryujinx.Common.Configuration.Hid.PlayerIndex;
|
|
using OriginalInputBackendType = Ryujinx.Common.Configuration.Hid.InputBackendType;
|
|
|
|
namespace Ryujinx.Graphics.Gpu.Overlay
|
|
{
|
|
/// <summary>
|
|
/// Controller overlay that shows controller bindings matching the original AXAML design
|
|
/// </summary>
|
|
public class ControllerOverlay : Overlay
|
|
{
|
|
private const float OverlayWidth = 400;
|
|
private const float Padding = 24;
|
|
private const float Spacing = 16;
|
|
private const float PlayerSpacing = 12;
|
|
private const float PlayerRowHeight = 32;
|
|
|
|
private const float TitleTextSize = 25;
|
|
private const float PlayerTextSize = 22;
|
|
|
|
private float _lifespan = 0f;
|
|
|
|
public ControllerOverlay() : base("ControllerOverlay")
|
|
{
|
|
CreateBaseElements();
|
|
}
|
|
|
|
private void CreateBaseElements()
|
|
{
|
|
// Main background container
|
|
var background = new RectangleElement(0, 0, OverlayWidth, 200, // Dynamic height will be set later
|
|
new SKColor(0, 0, 0, 224)) // #E0000000
|
|
{
|
|
Name = "Background",
|
|
CornerRadius = 12,
|
|
BorderColor = new SKColor(255, 255, 255, 64), // #40FFFFFF
|
|
BorderWidth = 1
|
|
};
|
|
AddElement(background);
|
|
|
|
// Title text
|
|
var titleText = new TextElement(Padding + 30, Padding, "Controller Bindings", TitleTextSize, SKColors.White)
|
|
{
|
|
Name = "TitleText",
|
|
FontStyle = SKFontStyle.Bold
|
|
};
|
|
AddElement(titleText);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show controller bindings matching the original AXAML implementation
|
|
/// </summary>
|
|
public void ShowControllerBindings(List<OriginalInputConfig> inputConfigs, int durationSeconds = 3)
|
|
{
|
|
// Reset lifespan and opacity
|
|
_lifespan = durationSeconds;
|
|
|
|
// Clear existing player bindings
|
|
ClearPlayerBindings();
|
|
|
|
// Debug: Log input data
|
|
// Group controllers by player index
|
|
var playerBindings = new Dictionary<OriginalPlayerIndex, List<OriginalInputConfig>>();
|
|
|
|
foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= OriginalPlayerIndex.Player4))
|
|
{
|
|
Console.WriteLine($"ControllerOverlay: Config for Player {config.PlayerIndex}: {config.Name} ({config.Backend})");
|
|
if (!playerBindings.ContainsKey(config.PlayerIndex))
|
|
{
|
|
playerBindings[config.PlayerIndex] = new List<OriginalInputConfig>();
|
|
}
|
|
playerBindings[config.PlayerIndex].Add(config);
|
|
}
|
|
|
|
float currentY = Padding + 40; // After title
|
|
|
|
// Add player bindings to UI
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
var playerIndex = (OriginalPlayerIndex)i;
|
|
float rowY = currentY + (i * (PlayerRowHeight + PlayerSpacing));
|
|
|
|
// Player number with colored background (circular badge)
|
|
var playerColor = GetPlayerColor(i);
|
|
var playerBadge = new RectangleElement(Padding, rowY, 24, 20, playerColor)
|
|
{
|
|
Name = $"PlayerBadge_{i}",
|
|
CornerRadius = 12
|
|
};
|
|
AddElement(playerBadge);
|
|
|
|
// Player number text
|
|
var playerLabel = new TextElement(Padding + 12, rowY + 2, $"P{i + 1}", PlayerTextSize, SKColors.White)
|
|
{
|
|
Name = $"PlayerLabel_{i}",
|
|
FontStyle = SKFontStyle.Bold,
|
|
TextAlign = SKTextAlign.Center
|
|
};
|
|
AddElement(playerLabel);
|
|
|
|
// Controller info
|
|
if (playerBindings.ContainsKey(playerIndex))
|
|
{
|
|
var controllers = playerBindings[playerIndex];
|
|
var controllerNames = controllers.Select(c => GetControllerDisplayName(c)).ToList();
|
|
|
|
var controllerText = new TextElement(Padding + 56, rowY + 2, string.Join(", ", controllerNames), PlayerTextSize, new SKColor(144, 238, 144)) // LightGreen
|
|
{
|
|
Name = $"ControllerText_{i}",
|
|
FontStyle = SKFontStyle.Bold
|
|
};
|
|
AddElement(controllerText);
|
|
}
|
|
else
|
|
{
|
|
var noControllerText = new TextElement(Padding + 56, rowY + 2, "No controller assigned", PlayerTextSize, new SKColor(128, 128, 128)) // Gray
|
|
{
|
|
Name = $"NoControllerText_{i}",
|
|
FontStyle = SKFontStyle.Italic
|
|
};
|
|
AddElement(noControllerText);
|
|
}
|
|
}
|
|
|
|
// Calculate total height and update background
|
|
float totalHeight = Padding + 40 + (4 * (PlayerRowHeight + PlayerSpacing)) + Padding + 40; // Extra space for duration text
|
|
var background = FindElement<RectangleElement>("Background");
|
|
if (background != null)
|
|
{
|
|
background.Height = totalHeight;
|
|
}
|
|
|
|
// Duration text at bottom
|
|
string durationText = durationSeconds == 1
|
|
? "This overlay will disappear in 1 second"
|
|
: $"This overlay will disappear in {durationSeconds} seconds";
|
|
|
|
// Show the overlay (position will be set by Window class with actual dimensions)
|
|
IsVisible = true;
|
|
}
|
|
|
|
private static SKColor GetPlayerColor(int playerIndex)
|
|
{
|
|
return playerIndex switch
|
|
{
|
|
0 => new SKColor(255, 92, 92), // Red for Player 1
|
|
1 => new SKColor(54, 162, 235), // Blue for Player 2
|
|
2 => new SKColor(255, 206, 84), // Yellow for Player 3
|
|
3 => new SKColor(75, 192, 192), // Green for Player 4
|
|
_ => new SKColor(128, 128, 128) // Gray fallback
|
|
};
|
|
}
|
|
|
|
private static string GetControllerDisplayName(OriginalInputConfig config)
|
|
{
|
|
if (string.IsNullOrEmpty(config.Name))
|
|
{
|
|
return config.Backend switch
|
|
{
|
|
OriginalInputBackendType.WindowKeyboard => "Keyboard",
|
|
OriginalInputBackendType.GamepadSDL2 => "Controller",
|
|
_ => "Unknown"
|
|
};
|
|
}
|
|
|
|
// Truncate long controller names
|
|
string name = config.Name;
|
|
if (name.Length > 25)
|
|
{
|
|
name = name.Substring(0, 22) + "...";
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear all player bindings
|
|
/// </summary>
|
|
private void ClearPlayerBindings()
|
|
{
|
|
var elementsToRemove = new List<OverlayElement>();
|
|
|
|
foreach (var element in GetElements())
|
|
{
|
|
if (element.Name.StartsWith("PlayerBadge_") ||
|
|
element.Name.StartsWith("PlayerLabel_") ||
|
|
element.Name.StartsWith("ControllerText_") ||
|
|
element.Name.StartsWith("NoControllerText_"))
|
|
{
|
|
elementsToRemove.Add(element);
|
|
}
|
|
}
|
|
|
|
foreach (var element in elementsToRemove)
|
|
{
|
|
RemoveElement(element);
|
|
element.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update overlay
|
|
/// </summary>
|
|
public override void Update(float deltaTime, SKSize screenSize)
|
|
{
|
|
_lifespan -= deltaTime;
|
|
|
|
if (_lifespan <= 0)
|
|
{
|
|
IsVisible = false;
|
|
return;
|
|
}
|
|
|
|
if (_lifespan <= 0.5f)
|
|
{
|
|
// Fade out during the last 0.5 seconds
|
|
Opacity = _lifespan / 0.5f;
|
|
}
|
|
else
|
|
{
|
|
Opacity = 1;
|
|
}
|
|
|
|
// Update position if screen size is provided
|
|
if (screenSize.Width > 0 && screenSize.Height > 0)
|
|
{
|
|
SetPositionToTopRight(screenSize.Width, screenSize.Height);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Position overlay to top-right matching original AXAML positioning
|
|
/// </summary>
|
|
public void SetPositionToTopRight(float screenWidth, float screenHeight)
|
|
{
|
|
X = screenWidth - OverlayWidth - 20; // 20px margin from right
|
|
Y = 50; // 50px margin from top
|
|
}
|
|
}
|
|
}
|