(Feat) Add Remmaping

This commit is contained in:
2026-06-26 16:23:20 +02:00
parent 931bc9c9e2
commit 7bd2aa3120
249 changed files with 23712 additions and 6898 deletions
@@ -1,68 +1,59 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ashwild.Settings
{
/// <summary>
/// Binds the audio volume widgets (master, music, SFX) to the <see cref="SettingsManager"/>,
/// configuring each slider from the authored <see cref="SettingsDefinition"/>.
/// </summary>
public class AudioSettingsSubPanel : SettingsSubPanel
{
[Header("Master")]
[SerializeField] private Slider masterSlider;
[SerializeField] private TMP_Text masterLabel;
#region Serialized Fields
[Header("Music")]
[SerializeField] private Slider musicSlider;
[SerializeField] private TMP_Text musicLabel;
[Header("Widgets")]
[SerializeField] private SettingSliderWidget masterSlider;
[SerializeField] private SettingSliderWidget musicSlider;
[SerializeField] private SettingSliderWidget sfxSlider;
[Header("SFX")]
[SerializeField] private Slider sfxSlider;
[SerializeField] private TMP_Text sfxLabel;
#endregion
private bool wiring;
#region Unity Lifecycle
/// <summary>
/// Initializes each volume slider from the definition with its apply action.
/// </summary>
private void Awake()
{
masterSlider.onValueChanged.AddListener(OnMasterChanged);
musicSlider.onValueChanged.AddListener(OnMusicChanged);
sfxSlider.onValueChanged.AddListener(OnSfxChanged);
SettingsManager s = SettingsManager.Instance;
if (s == null || s.Definition == null)
{
Debug.LogError($"[AudioSettingsSubPanel] SettingsManager/Definition unavailable on '{name}'.", this);
return;
}
SettingsDefinition def = s.Definition;
masterSlider.Init(def.MasterVolume.Min, def.MasterVolume.Max, def.MasterVolume.LabelScale, def.MasterVolume.LabelFormat, v => s.SetMasterVolume(v));
musicSlider.Init(def.MusicVolume.Min, def.MusicVolume.Max, def.MusicVolume.LabelScale, def.MusicVolume.LabelFormat, v => s.SetMusicVolume(v));
sfxSlider.Init(def.SfxVolume.Min, def.SfxVolume.Max, def.SfxVolume.LabelScale, def.SfxVolume.LabelFormat, v => s.SetSfxVolume(v));
}
#endregion
#region Public API
/// <summary>
/// Pushes the manager's current volumes into the sliders.
/// </summary>
public override void Refresh()
{
SettingsManager s = SettingsManager.Instance;
if (s == null) return;
wiring = true;
masterSlider.SetValueWithoutNotify(s.MasterVolume);
musicSlider.SetValueWithoutNotify(s.MusicVolume);
sfxSlider.SetValueWithoutNotify(s.SfxVolume);
UpdateLabel(masterLabel, s.MasterVolume);
UpdateLabel(musicLabel, s.MusicVolume);
UpdateLabel(sfxLabel, s.SfxVolume);
wiring = false;
masterSlider.SetValue(s.MasterVolume);
musicSlider.SetValue(s.MusicVolume);
sfxSlider.SetValue(s.SfxVolume);
}
private void OnMasterChanged(float v)
{
UpdateLabel(masterLabel, v);
if (!wiring) SettingsManager.Instance.SetMasterVolume(v);
}
private void OnMusicChanged(float v)
{
UpdateLabel(musicLabel, v);
if (!wiring) SettingsManager.Instance.SetMusicVolume(v);
}
private void OnSfxChanged(float v)
{
UpdateLabel(sfxLabel, v);
if (!wiring) SettingsManager.Instance.SetSfxVolume(v);
}
private static void UpdateLabel(TMP_Text label, float value01)
{
if (label != null) label.text = $"{Mathf.RoundToInt(value01 * 100f)}%";
}
#endregion
}
}
@@ -1,43 +1,59 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ashwild.Settings
{
/// <summary>
/// Binds the control widgets (sensitivity, invert-Y, crosshair) to the <see cref="SettingsManager"/>,
/// configuring the sensitivity slider from the authored <see cref="SettingsDefinition"/>.
/// </summary>
public class ControlsSettingsSubPanel : SettingsSubPanel
{
[Header("Controls")]
[SerializeField] private Slider sensitivitySlider;
[SerializeField] private TMP_Text sensitivityLabel;
[SerializeField] private Toggle invertYToggle;
[SerializeField] private Toggle crosshairToggle;
#region Serialized Fields
private bool wiring;
[Header("Widgets")]
[SerializeField] private SettingSliderWidget sensitivitySlider;
[SerializeField] private SettingToggleWidget invertYToggle;
[SerializeField] private SettingToggleWidget crosshairToggle;
#endregion
#region Unity Lifecycle
/// <summary>
/// Initializes each control widget from the definition with its apply action.
/// </summary>
private void Awake()
{
sensitivitySlider.onValueChanged.AddListener(OnSensitivityChanged);
invertYToggle.onValueChanged.AddListener(v => { if (!wiring) SettingsManager.Instance.SetInvertY(v); });
crosshairToggle.onValueChanged.AddListener(v => { if (!wiring) SettingsManager.Instance.SetCrosshairEnabled(v); });
SettingsManager s = SettingsManager.Instance;
if (s == null || s.Definition == null)
{
Debug.LogError($"[ControlsSettingsSubPanel] SettingsManager/Definition unavailable on '{name}'.", this);
return;
}
SettingsDefinition def = s.Definition;
sensitivitySlider.Init(def.Sensitivity.Min, def.Sensitivity.Max, def.Sensitivity.LabelScale, def.Sensitivity.LabelFormat, v => s.SetSensitivity(v));
invertYToggle.Init(v => s.SetInvertY(v));
crosshairToggle.Init(v => s.SetCrosshairEnabled(v));
}
#endregion
#region Public API
/// <summary>
/// Pushes the manager's current control values into the widgets.
/// </summary>
public override void Refresh()
{
SettingsManager s = SettingsManager.Instance;
if (s == null) return;
wiring = true;
sensitivitySlider.SetValueWithoutNotify(s.Sensitivity);
if (sensitivityLabel != null) sensitivityLabel.text = s.Sensitivity.ToString("F2");
invertYToggle.SetIsOnWithoutNotify(s.InvertY);
crosshairToggle.SetIsOnWithoutNotify(s.CrosshairEnabled);
wiring = false;
sensitivitySlider.SetValue(s.Sensitivity);
invertYToggle.SetValue(s.InvertY);
crosshairToggle.SetValue(s.CrosshairEnabled);
}
private void OnSensitivityChanged(float v)
{
if (sensitivityLabel != null) sensitivityLabel.text = v.ToString("F2");
if (!wiring) SettingsManager.Instance.SetSensitivity(v);
}
#endregion
}
}
@@ -1,90 +1,115 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ashwild.Settings
{
/// <summary>
/// Binds the display widgets (resolution, fullscreen mode, monitor, brightness) to the
/// <see cref="SettingsManager"/>. The panel builds the dropdown option lists from the current
/// hardware and converts the fullscreen dropdown index to and from <see cref="FullScreenMode"/>;
/// the brightness slider is configured from the authored <see cref="SettingsDefinition"/>. The
/// widgets themselves stay pure UI.
/// </summary>
public class DisplaySettingsSubPanel : SettingsSubPanel
{
[Header("Controls")]
[SerializeField] private TMP_Dropdown resolutionDropdown;
[SerializeField] private TMP_Dropdown fullscreenDropdown;
[SerializeField] private TMP_Dropdown monitorDropdown;
[SerializeField] private Slider brightnessSlider;
[SerializeField] private TMP_Text brightnessLabel;
#region Serialized Fields
private bool wiring;
[Header("Widgets")]
[SerializeField] private SettingDropdownWidget resolutionDropdown;
[SerializeField] private SettingDropdownWidget fullscreenDropdown;
[SerializeField] private SettingDropdownWidget monitorDropdown;
[SerializeField] private SettingSliderWidget brightnessSlider;
#endregion
#region Unity Lifecycle
/// <summary>
/// Initializes each display widget with its apply action (brightness range from the definition).
/// </summary>
private void Awake()
{
resolutionDropdown.onValueChanged.AddListener(OnResolutionChanged);
fullscreenDropdown.onValueChanged.AddListener(OnFullscreenChanged);
monitorDropdown.onValueChanged.AddListener(OnMonitorChanged);
brightnessSlider.onValueChanged.AddListener(OnBrightnessChanged);
SettingsManager s = SettingsManager.Instance;
if (s == null || s.Definition == null)
{
Debug.LogError($"[DisplaySettingsSubPanel] SettingsManager/Definition unavailable on '{name}'.", this);
return;
}
SettingsDefinition def = s.Definition;
resolutionDropdown.Init(v => s.SetResolution(v));
fullscreenDropdown.Init(v => s.SetFullscreenMode(IndexToMode(v)));
monitorDropdown.Init(v => s.SetMonitor(v));
brightnessSlider.Init(def.Brightness.Min, def.Brightness.Max, def.Brightness.LabelScale, def.Brightness.LabelFormat, v => s.SetBrightness(v));
}
#endregion
#region Public API
/// <summary>
/// Rebuilds the dropdown options from current hardware and pushes the manager's values.
/// </summary>
public override void Refresh()
{
SettingsManager s = SettingsManager.Instance;
if (s == null) return;
wiring = true;
resolutionDropdown.SetOptions(BuildResolutionOptions());
resolutionDropdown.SetValue(s.ResolutionIndex);
PopulateResolutionDropdown();
resolutionDropdown.value = Mathf.Clamp(s.ResolutionIndex, 0, resolutionDropdown.options.Count - 1);
resolutionDropdown.RefreshShownValue();
fullscreenDropdown.SetOptions(FullscreenOptions);
fullscreenDropdown.SetValue(ModeToIndex(s.FullscreenMode));
PopulateFullscreenDropdown();
fullscreenDropdown.value = FullscreenModeToIndex(s.FullscreenMode);
fullscreenDropdown.RefreshShownValue();
monitorDropdown.SetOptions(BuildMonitorOptions());
monitorDropdown.SetValue(s.MonitorIndex);
PopulateMonitorDropdown();
monitorDropdown.value = Mathf.Clamp(s.MonitorIndex, 0, monitorDropdown.options.Count - 1);
monitorDropdown.RefreshShownValue();
brightnessSlider.SetValueWithoutNotify(s.Brightness);
if (brightnessLabel != null) brightnessLabel.text = $"{Mathf.RoundToInt(s.Brightness * 100f)}%";
wiring = false;
brightnessSlider.SetValue(s.Brightness);
}
private void PopulateResolutionDropdown()
#endregion
#region Internal Helpers
private static readonly string[] FullscreenOptions =
{
"Exclusive Fullscreen",
"Borderless Window",
"Maximized Window",
"Windowed"
};
/// <summary>
/// Builds the human-readable resolution list from the monitor's supported modes.
/// </summary>
private static List<string> BuildResolutionOptions()
{
resolutionDropdown.ClearOptions();
List<string> options = new List<string>();
foreach (Resolution r in Screen.resolutions)
options.Add($"{r.width}×{r.height} @{Mathf.RoundToInt((float)r.refreshRateRatio.value)}Hz");
resolutionDropdown.AddOptions(options);
return options;
}
private void PopulateFullscreenDropdown()
/// <summary>
/// Builds the monitor list from the current display layout.
/// </summary>
private static List<string> BuildMonitorOptions()
{
fullscreenDropdown.ClearOptions();
fullscreenDropdown.AddOptions(new List<string>
{
"Exclusive Fullscreen",
"Borderless Window",
"Maximized Window",
"Windowed"
});
}
private void PopulateMonitorDropdown()
{
monitorDropdown.ClearOptions();
List<DisplayInfo> displays = new List<DisplayInfo>();
Screen.GetDisplayLayout(displays);
List<string> options = new List<string>();
for (int i = 0; i < displays.Count; i++)
{
string name = string.IsNullOrEmpty(displays[i].name) ? $"Monitor {i + 1}" : displays[i].name;
options.Add($"{name} ({displays[i].width}×{displays[i].height})");
string label = string.IsNullOrEmpty(displays[i].name) ? $"Monitor {i + 1}" : displays[i].name;
options.Add($"{label} ({displays[i].width}×{displays[i].height})");
}
monitorDropdown.AddOptions(options);
return options;
}
private static int FullscreenModeToIndex(FullScreenMode mode) => mode switch
/// <summary>
/// Maps a fullscreen mode to its dropdown index.
/// </summary>
private static int ModeToIndex(FullScreenMode mode) => mode switch
{
FullScreenMode.ExclusiveFullScreen => 0,
FullScreenMode.FullScreenWindow => 1,
@@ -93,7 +118,10 @@ namespace Ashwild.Settings
_ => 1
};
private static FullScreenMode IndexToFullscreenMode(int index) => index switch
/// <summary>
/// Maps a dropdown index back to its fullscreen mode.
/// </summary>
private static FullScreenMode IndexToMode(int index) => index switch
{
0 => FullScreenMode.ExclusiveFullScreen,
1 => FullScreenMode.FullScreenWindow,
@@ -102,13 +130,6 @@ namespace Ashwild.Settings
_ => FullScreenMode.FullScreenWindow
};
private void OnResolutionChanged(int v) { if (!wiring) SettingsManager.Instance.SetResolution(v); }
private void OnFullscreenChanged(int v) { if (!wiring) SettingsManager.Instance.SetFullscreenMode(IndexToFullscreenMode(v)); }
private void OnMonitorChanged(int v) { if (!wiring) SettingsManager.Instance.SetMonitor(v); }
private void OnBrightnessChanged(float v)
{
if (brightnessLabel != null) brightnessLabel.text = $"{Mathf.RoundToInt(v * 100f)}%";
if (!wiring) SettingsManager.Instance.SetBrightness(v);
}
#endregion
}
}
@@ -1,64 +1,66 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Ashwild.Settings
{
/// <summary>
/// Binds the graphics widgets (quality, vsync, FPS cap, FOV) to the <see cref="SettingsManager"/>.
/// The panel is the only layer that knows what each widget means: it reads the authored
/// configuration from the manager's <see cref="SettingsDefinition"/> and initializes each widget
/// with its data and apply action, then pushes the current value on refresh. The widgets stay
/// pure UI.
/// </summary>
public class GraphicsSettingsSubPanel : SettingsSubPanel
{
[Header("Controls")]
[SerializeField] private TMP_Dropdown qualityDropdown;
[SerializeField] private Toggle vsyncToggle;
[SerializeField] private Slider targetFpsSlider;
[SerializeField] private TMP_Text targetFpsLabel;
[SerializeField] private Slider fovSlider;
[SerializeField] private TMP_Text fovLabel;
#region Serialized Fields
private bool wiring;
[Header("Widgets")]
[SerializeField] private SettingDropdownWidget qualityDropdown;
[SerializeField] private SettingToggleWidget vsyncToggle;
[SerializeField] private SettingStepperWidget targetFpsStepper;
[SerializeField] private SettingSliderWidget fovSlider;
#endregion
#region Unity Lifecycle
/// <summary>
/// Initializes each widget from the definition with the action to apply to the manager.
/// </summary>
private void Awake()
{
qualityDropdown.onValueChanged.AddListener(v => { if (!wiring) SettingsManager.Instance.SetQualityLevel(v); });
vsyncToggle.onValueChanged.AddListener(v => { if (!wiring) SettingsManager.Instance.SetVSync(v); });
targetFpsSlider.onValueChanged.AddListener(OnTargetFpsChanged);
fovSlider.onValueChanged.AddListener(OnFovChanged);
SettingsManager s = SettingsManager.Instance;
if (s == null || s.Definition == null)
{
Debug.LogError($"[GraphicsSettingsSubPanel] SettingsManager/Definition unavailable on '{name}'.", this);
return;
}
SettingsDefinition def = s.Definition;
qualityDropdown.Init(v => s.SetQualityLevel(v));
vsyncToggle.Init(v => s.SetVSync(v));
targetFpsStepper.Init(def.TargetFps.ToWidgetPresets(), false, v => s.SetTargetFps(v));
fovSlider.Init(def.Fov.Min, def.Fov.Max, def.Fov.LabelScale, def.Fov.LabelFormat, v => s.SetFov(v));
}
#endregion
#region Public API
/// <summary>
/// Pushes the manager's current graphics values into the widgets.
/// </summary>
public override void Refresh()
{
SettingsManager s = SettingsManager.Instance;
if (s == null) return;
wiring = true;
qualityDropdown.ClearOptions();
qualityDropdown.AddOptions(new List<string>(QualitySettings.names));
qualityDropdown.value = Mathf.Clamp(s.QualityLevel, 0, qualityDropdown.options.Count - 1);
qualityDropdown.RefreshShownValue();
vsyncToggle.SetIsOnWithoutNotify(s.VSync);
targetFpsSlider.SetValueWithoutNotify(s.TargetFps);
if (targetFpsLabel != null) targetFpsLabel.text = $"{s.TargetFps} FPS";
fovSlider.SetValueWithoutNotify(s.Fov);
if (fovLabel != null) fovLabel.text = $"{s.Fov:F0}°";
wiring = false;
qualityDropdown.SetOptions(QualitySettings.names);
qualityDropdown.SetValue(s.QualityLevel);
vsyncToggle.SetValue(s.VSync);
targetFpsStepper.SetValue(s.TargetFps);
fovSlider.SetValue(s.Fov);
}
private void OnTargetFpsChanged(float v)
{
int fps = Mathf.RoundToInt(v);
if (targetFpsLabel != null) targetFpsLabel.text = $"{fps} FPS";
if (!wiring) SettingsManager.Instance.SetTargetFps(fps);
}
private void OnFovChanged(float v)
{
if (fovLabel != null) fovLabel.text = $"{v:F0}°";
if (!wiring) SettingsManager.Instance.SetFov(v);
}
#endregion
}
}
@@ -0,0 +1,303 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Ashwild.Player;
namespace Ashwild.Settings
{
/// <summary>
/// Settings sub-panel that lists every rebindable binding from the shared InputConfig as rows and
/// drives interactive rebinds. It is the orchestrator: the InputManager performs the rebind on the
/// asset, the SettingsManager persists it, and this panel refreshes the rows. It also owns the
/// modal rebind prompt (a "press a key" overlay that becomes a "X already uses this key →
/// Replace / Cancel" conflict prompt) — the prompt objects live under the SettingsPanel so they
/// render in front of everything. Rows show layout-aware glyphs, so AZERTY reads correctly.
/// </summary>
public class KeybindingsSettingsSubPanel : SettingsSubPanel
{
#region Serialized Fields
[Header("Row List")]
[SerializeField] private Transform rowContainer;
[SerializeField] private GameObject rowPrefab;
[SerializeField] private Button resetAllButton;
[Header("Rebind Prompt")]
[Tooltip("Standalone prompt overlay — its own object under the SettingsPanel, not a child here.")]
[SerializeField] private RebindPromptUI prompt;
#endregion
#region State
private readonly List<KeybindRowUI> rows = new List<KeybindRowUI>();
private bool built;
/// <summary>
/// The shared input config, pulled from the SettingsManager (assigned there, not here).
/// </summary>
private InputConfig config;
/// <summary>
/// The conflict awaiting the player's Replace/Cancel choice, kept so the buttons can resolve it.
/// </summary>
private InputManager.RebindOutcome pendingOutcome;
private bool hasPendingConflict;
private KeybindRowUI activeRow;
#endregion
#region Unity Lifecycle
/// <summary>
/// Wires the reset-all button and the prompt's Replace/Cancel buttons.
/// </summary>
private void Awake()
{
if (resetAllButton != null) resetAllButton.onClick.AddListener(ResetAll);
}
/// <summary>
/// Aborts any rebind/conflict in flight when the panel is hidden (category switch / closed).
/// </summary>
private void OnDisable()
{
if (InputManager.Instance != null) InputManager.Instance.CancelOngoingRebind();
if (hasPendingConflict) OnConflictCancel();
HidePrompt();
}
#endregion
#region Public API
/// <summary>
/// Builds the rows once, then repaints every current key (so reopening shows live bindings).
/// </summary>
public override void Refresh()
{
config = SettingsManager.Instance != null ? SettingsManager.Instance.InputConfig : null;
if (config == null)
{
Debug.LogError("[KeybindingsSettingsSubPanel] No InputConfig available from SettingsManager.", this);
return;
}
HidePrompt();
if (!built) BuildRows();
RefreshAll();
}
#endregion
#region Row Building
/// <summary>
/// Instantiates one row per config binding and binds its callbacks.
/// </summary>
private void BuildRows()
{
if (rowPrefab == null || rowContainer == null)
{
Debug.LogError($"[KeybindingsSettingsSubPanel] Row prefab/container missing on '{name}'.", this);
return;
}
foreach (RebindableEntry entry in config.Bindings)
{
GameObject go = Instantiate(rowPrefab, rowContainer);
KeybindRowUI row = go.GetComponent<KeybindRowUI>();
if (row == null)
{
Debug.LogError("[KeybindingsSettingsSubPanel] Row prefab has no KeybindRowUI.", this);
Destroy(go);
continue;
}
row.Initialize(entry, OnRowRebind, OnRowReset);
rows.Add(row);
}
built = true;
}
#endregion
#region Row Callbacks
/// <summary>
/// Opens the prompt and starts capturing a new key for the clicked row.
/// </summary>
private void OnRowRebind(KeybindRowUI row)
{
if (InputManager.Instance == null) return;
activeRow = row;
ShowListening();
InputManager.Instance.StartInteractiveRebind(row.ActionName, row.BindingIndex, OnRebindOutcome);
}
/// <summary>
/// Handles the capture result: persist + refresh on success, raise the conflict prompt on a
/// clash, or just close on cancel.
/// </summary>
private void OnRebindOutcome(InputManager.RebindOutcome outcome)
{
switch (outcome.Result)
{
case InputManager.RebindResult.Completed:
HidePrompt();
Persist();
RefreshAll();
break;
case InputManager.RebindResult.Conflict:
pendingOutcome = outcome;
hasPendingConflict = true;
ShowConflict(LabelFor(outcome.ConflictActionName));
break;
default:
HidePrompt();
if (activeRow != null) RefreshRow(activeRow);
break;
}
}
/// <summary>
/// Resolves a conflict by swapping the two keys (the other action takes this one's old key).
/// </summary>
private void OnConflictReplace()
{
if (!hasPendingConflict) return;
if (InputManager.Instance != null) InputManager.Instance.ApplySwap(pendingOutcome);
ClearConflict();
HidePrompt();
Persist();
RefreshAll();
}
/// <summary>
/// Resolves a conflict by undoing the new binding (the other action keeps its key).
/// </summary>
private void OnConflictCancel()
{
if (!hasPendingConflict) return;
if (InputManager.Instance != null) InputManager.Instance.RevertRebind(pendingOutcome);
ClearConflict();
HidePrompt();
RefreshAll();
}
/// <summary>
/// Resets the clicked row's binding to default and persists.
/// </summary>
private void OnRowReset(KeybindRowUI row)
{
if (InputManager.Instance == null) return;
InputManager.Instance.ResetBinding(row.ActionName, row.BindingIndex);
Persist();
RefreshRow(row);
}
/// <summary>
/// Resets every binding to default and persists.
/// </summary>
private void ResetAll()
{
if (InputManager.Instance == null) return;
InputManager.Instance.ResetAllBindings();
Persist();
RefreshAll();
}
#endregion
#region Prompt
/// <summary>
/// Shows the prompt in "press a key" mode.
/// </summary>
private void ShowListening()
{
if (prompt != null) prompt.ShowListening();
}
/// <summary>
/// Switches the open prompt to the conflict state for the named action, wiring its buttons.
/// </summary>
private void ShowConflict(string conflictLabel)
{
if (prompt != null) prompt.ShowConflict(conflictLabel, OnConflictReplace, OnConflictCancel);
}
/// <summary>
/// Hides the prompt entirely.
/// </summary>
private void HidePrompt()
{
if (prompt != null) prompt.Hide();
}
/// <summary>
/// Clears the pending-conflict state.
/// </summary>
private void ClearConflict()
{
hasPendingConflict = false;
pendingOutcome = default;
}
#endregion
#region Refresh
/// <summary>
/// Persists the current bindings through the SettingsManager.
/// </summary>
private void Persist()
{
if (SettingsManager.Instance != null) SettingsManager.Instance.SaveKeybinds();
}
/// <summary>
/// Repaints the current key on every row.
/// </summary>
private void RefreshAll()
{
foreach (KeybindRowUI row in rows) RefreshRow(row);
}
/// <summary>
/// Repaints one row's current key, choosing a glyph from the config when available.
/// </summary>
private void RefreshRow(KeybindRowUI row)
{
if (InputManager.Instance == null) return;
string display = InputManager.Instance.GetBindingDisplayString(row.ActionName, row.BindingIndex);
Sprite icon = config != null ? config.GetIcon(display) : null;
if (icon == null && config != null)
{
string controlPath = InputManager.Instance.GetBindingControlPath(row.ActionName, row.BindingIndex);
icon = config.GetIcon(controlPath);
}
row.RefreshDisplay(display, icon);
}
/// <summary>
/// The display label authored for an action (first matching binding), or the raw name.
/// </summary>
private string LabelFor(string actionName)
{
if (config != null)
foreach (RebindableEntry entry in config.Bindings)
if (entry.ActionName == actionName)
return entry.DisplayLabel;
return actionName;
}
#endregion
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 56f3db8af01bac245822790db109b980