Files
Fishing2/Assets/ThirdParty/Rewired/Examples/ControlRemapping1/Scripts/ControlRemappingDemo1.cs
2025-05-10 18:06:44 +08:00

1775 lines
70 KiB
C#

// Copyright (c) 2017 Augie R. Maddox, Guavaman Enterprises. All rights reserved.
#pragma warning disable 0649 // disable warnings about unused variables
namespace Rewired.Demos {
using UnityEngine;
using System;
using System.Collections.Generic;
using Rewired;
[AddComponentMenu("")]
public class ControlRemappingDemo1 : MonoBehaviour {
private const float defaultModalWidth = 250.0f;
private const float defaultModalHeight = 200.0f;
private const float assignmentTimeout = 5.0f;
// Helper objects
private DialogHelper dialog;
// Listener
private InputMapper inputMapper = new InputMapper();
private InputMapper.ConflictFoundEventData conflictFoundEventData;
// GUI state management
private bool guiState;
private bool busy;
private bool pageGUIState;
// Selections
private Player selectedPlayer;
private int selectedMapCategoryId;
private ControllerSelection selectedController;
ControllerMap selectedMap;
// Other flags
private bool showMenu;
private bool startListening;
// Scroll view positions
private Vector2 actionScrollPos;
private Vector2 calibrateScrollPos;
// Queues
private Queue<QueueEntry> actionQueue;
// Setup vars
private bool setupFinished;
// Editor state management
[System.NonSerialized]
private bool initialized;
private bool isCompiling;
// Styles
GUIStyle style_wordWrap;
GUIStyle style_centeredBox;
#region Initialization
private void Awake() {
inputMapper.options.timeout = assignmentTimeout;
inputMapper.options.ignoreMouseXAxis = true;
inputMapper.options.ignoreMouseYAxis = true;
Initialize();
}
private void OnEnable() {
Subscribe();
}
private void OnDisable() {
Unsubscribe();
}
private void Initialize() {
dialog = new DialogHelper();
actionQueue = new Queue<QueueEntry>();
selectedController = new ControllerSelection();
ReInput.ControllerConnectedEvent += JoystickConnected;
ReInput.ControllerPreDisconnectEvent += JoystickPreDisconnect; // runs before joystick is completely disconnected so we can save maps
ReInput.ControllerDisconnectedEvent += JoystickDisconnected; // final disconnect that runs after joystick has been fully removed
ResetAll();
initialized = true;
ReInput.userDataStore.Load(); // load saved user maps on start if there are any to load
if(ReInput.unityJoystickIdentificationRequired) {
IdentifyAllJoysticks();
}
}
private void Setup() {
if(setupFinished) return;
// Create styles
style_wordWrap = new GUIStyle(GUI.skin.label);
style_wordWrap.wordWrap = true;
style_centeredBox = new GUIStyle(GUI.skin.box);
style_centeredBox.alignment = TextAnchor.MiddleCenter;
setupFinished = true;
}
private void Subscribe() {
Unsubscribe();
inputMapper.ConflictFoundEvent += OnConflictFound;
inputMapper.StoppedEvent += OnStopped;
}
private void Unsubscribe() {
inputMapper.RemoveAllEventListeners();
}
#endregion
#region Main Update
public void OnGUI() {
#if UNITY_EDITOR
// Check for script recompile in the editor
CheckRecompile();
#endif
if(!initialized) return;
Setup();
HandleMenuControl();
if(!showMenu) {
DrawInitialScreen();
return;
}
SetGUIStateStart();
// Process queue
ProcessQueue();
// Draw contents
DrawPage();
ShowDialog();
SetGUIStateEnd();
// Clear momentary vars
busy = false;
}
#endregion
#region Menu Control
private void HandleMenuControl() {
if(dialog.enabled) return; // don't allow closing the menu while dialog is open so there won't be issues remapping the Menu button
if(Event.current.type == EventType.Layout) {
if(ReInput.players.GetSystemPlayer().GetButtonDown("Menu")) {
if(showMenu) { // menu is open and will be closed
ReInput.userDataStore.Save(); // save all maps when menu is closed
Close();
} else {
Open();
}
}
}
}
private void Close() {
ClearWorkingVars();
showMenu = false;
}
private void Open() {
showMenu = true;
}
#endregion
#region Draw
private void DrawInitialScreen() {
GUIContent content;
ActionElementMap map = ReInput.players.GetSystemPlayer().controllers.maps.GetFirstElementMapWithAction("Menu", true);
if(map != null) {
content = new GUIContent("Press " + map.elementIdentifierName + " to open the menu.");
} else {
content = new GUIContent("There is no element assigned to open the menu!");
}
// Draw the box
GUILayout.BeginArea(GetScreenCenteredRect(300, 50));
GUILayout.Box(content, style_centeredBox, GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
GUILayout.EndArea();
}
private void DrawPage() {
if(GUI.enabled != pageGUIState) GUI.enabled = pageGUIState;
Rect screenRect = new Rect((Screen.width - (Screen.width * 0.9f)) * 0.5f, (Screen.height - (Screen.height * 0.9f)) * 0.5f, Screen.width * 0.9f, Screen.height * 0.9f);
GUILayout.BeginArea(screenRect);
// Player Selector
DrawPlayerSelector();
// Joystick Selector
DrawJoystickSelector();
// Mouse
DrawMouseAssignment();
// Controllers
DrawControllerSelector();
// Controller Calibration
DrawCalibrateButton();
// Categories
DrawMapCategories();
// Create scroll view
actionScrollPos = GUILayout.BeginScrollView(actionScrollPos);
// Actions
DrawCategoryActions();
GUILayout.EndScrollView();
GUILayout.EndArea();
}
private void DrawPlayerSelector() {
if(ReInput.players.allPlayerCount == 0) {
GUILayout.Label("There are no players.");
return;
}
GUILayout.Space(15);
GUILayout.Label("Players:");
GUILayout.BeginHorizontal();
foreach(Player player in ReInput.players.GetPlayers(true)) {
if(selectedPlayer == null) selectedPlayer = player; // if no player selected, select first
bool prevValue = player == selectedPlayer ? true : false;
bool value = GUILayout.Toggle(prevValue, player.descriptiveName != string.Empty ? player.descriptiveName : player.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) { // value changed
if(value) { // selected
selectedPlayer = player;
selectedController.Clear(); // reset the device selection
selectedMapCategoryId = -1; // clear category selection
} // do not allow deselection
}
}
GUILayout.EndHorizontal();
}
private void DrawMouseAssignment() {
bool origGuiEnabled = GUI.enabled; // save GUI state
if(selectedPlayer == null) GUI.enabled = false;
GUILayout.Space(15);
GUILayout.Label("Assign Mouse:");
GUILayout.BeginHorizontal();
bool prevValue = selectedPlayer != null && selectedPlayer.controllers.hasMouse ? true : false;
bool value = GUILayout.Toggle(prevValue, "Assign Mouse", "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) { // user clicked
if(value) {
selectedPlayer.controllers.hasMouse = true;
foreach(Player player in ReInput.players.Players) { // de-assign mouse from all players except System
if(player == selectedPlayer) continue; // skip self
player.controllers.hasMouse = false;
}
} else {
selectedPlayer.controllers.hasMouse = false;
}
}
GUILayout.EndHorizontal();
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
private void DrawJoystickSelector() {
bool origGuiEnabled = GUI.enabled; // save GUI state
if(selectedPlayer == null) GUI.enabled = false;
GUILayout.Space(15);
GUILayout.Label("Assign Joysticks:");
GUILayout.BeginHorizontal();
bool prevValue = selectedPlayer == null || selectedPlayer.controllers.joystickCount == 0 ? true : false;
bool value = GUILayout.Toggle(prevValue, "None", "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) { // user clicked
selectedPlayer.controllers.ClearControllersOfType(ControllerType.Joystick);
ControllerSelectionChanged();
// do not allow deselection
}
if(selectedPlayer != null) {
foreach(Joystick joystick in ReInput.controllers.Joysticks) {
prevValue = selectedPlayer.controllers.ContainsController(joystick);
value = GUILayout.Toggle(prevValue, joystick.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) { // user clicked
EnqueueAction(new JoystickAssignmentChange(selectedPlayer.id, joystick.id, value));
}
}
}
GUILayout.EndHorizontal();
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
private void DrawControllerSelector() {
if(selectedPlayer == null) return;
bool origGuiEnabled = GUI.enabled; // save GUI state
GUILayout.Space(15);
GUILayout.Label("Controller to Map:");
GUILayout.BeginHorizontal();
bool value, prevValue;
if(!selectedController.hasSelection) {
selectedController.Set(0, ControllerType.Keyboard); // select keyboard if nothing selected
ControllerSelectionChanged();
}
// Keyboard
prevValue = selectedController.type == ControllerType.Keyboard;
value = GUILayout.Toggle(prevValue, ReInput.controllers.Keyboard.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) {
selectedController.Set(0, ControllerType.Keyboard); // set current selected device to this
ControllerSelectionChanged();
}
// Mouse
if(!selectedPlayer.controllers.hasMouse) GUI.enabled = false; // disable mouse if player doesn't have mouse assigned
prevValue = selectedController.type == ControllerType.Mouse;
value = GUILayout.Toggle(prevValue, ReInput.controllers.Mouse.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) {
selectedController.Set(0, ControllerType.Mouse); // set current selected device to this
ControllerSelectionChanged();
}
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // re-enable gui
// Joystick List
foreach(Joystick j in selectedPlayer.controllers.Joysticks) {
prevValue = selectedController.type == ControllerType.Joystick && selectedController.id == j.id;
value = GUILayout.Toggle(prevValue, j.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) {
selectedController.Set(j.id, ControllerType.Joystick); // set current selected device to this
ControllerSelectionChanged();
}
}
GUILayout.EndHorizontal();
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
private void DrawCalibrateButton() {
if(selectedPlayer == null) return;
bool origGuiEnabled = GUI.enabled; // save GUI state
GUILayout.Space(10);
Controller controller = selectedController.hasSelection ? selectedPlayer.controllers.GetController(selectedController.type, selectedController.id) : null;
if(controller == null || selectedController.type != ControllerType.Joystick) {
GUI.enabled = false;
GUILayout.Button("Select a controller to calibrate", GUILayout.ExpandWidth(false));
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled;
} else { // Calibrate joystick
if(GUILayout.Button("Calibrate " + controller.name, GUILayout.ExpandWidth(false))) {
Joystick joystick = controller as Joystick;
if(joystick != null) {
CalibrationMap calibrationMap = joystick.calibrationMap;
if(calibrationMap != null) {
EnqueueAction(new Calibration(selectedPlayer, joystick, calibrationMap));
}
}
}
}
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
private void DrawMapCategories() {
if(selectedPlayer == null) return;
if(!selectedController.hasSelection) return;
bool origGuiEnabled = GUI.enabled; // save GUI state
GUILayout.Space(15);
GUILayout.Label("Categories:");
GUILayout.BeginHorizontal();
foreach(InputMapCategory category in ReInput.mapping.UserAssignableMapCategories) {
if(!selectedPlayer.controllers.maps.ContainsMapInCategory(selectedController.type, category.id)) { // if player has no maps in this category for controller don't allow them to select it
GUI.enabled = false;
} else {
// Select first available category if none selected
if(selectedMapCategoryId < 0) {
selectedMapCategoryId = category.id; // if no category selected, select first
selectedMap = selectedPlayer.controllers.maps.GetFirstMapInCategory(selectedController.type, selectedController.id, category.id);
}
}
bool prevValue = category.id == selectedMapCategoryId ? true : false;
bool value = GUILayout.Toggle(prevValue, category.descriptiveName != string.Empty ? category.descriptiveName : category.name, "Button", GUILayout.ExpandWidth(false));
if(value != prevValue) { // category changed
selectedMapCategoryId = category.id;
selectedMap = selectedPlayer.controllers.maps.GetFirstMapInCategory(selectedController.type, selectedController.id, category.id);
}
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled;
}
GUILayout.EndHorizontal();
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
private void DrawCategoryActions() {
if(selectedPlayer == null) return;
if(selectedMapCategoryId < 0) return;
bool origGuiEnabled = GUI.enabled; // save GUI state
if(selectedMap == null) return; // controller map does not exist for this category in this controller
GUILayout.Space(15);
GUILayout.Label("Actions:");
InputMapCategory mapCategory = ReInput.mapping.GetMapCategory(selectedMapCategoryId); // get the selected map category
if(mapCategory == null) return;
InputCategory actionCategory = ReInput.mapping.GetActionCategory(mapCategory.name); // get the action category with the same name
if(actionCategory == null) return; // no action category exists with the same name
float labelWidth = 150.0f;
// Draw the list of actions for the selected action category
foreach(InputAction action in ReInput.mapping.ActionsInCategory(actionCategory.id)) {
string name = action.descriptiveName != string.Empty ? action.descriptiveName : action.name;
if(action.type == InputActionType.Button) {
GUILayout.BeginHorizontal();
GUILayout.Label(name, GUILayout.Width(labelWidth));
DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap); // Add assignment button
// Write out assigned elements
foreach(ActionElementMap elementMap in selectedMap.AllMaps) {
if(elementMap.actionId != action.id) continue;
DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap, elementMap);
}
GUILayout.EndHorizontal();
} else if(action.type == InputActionType.Axis) { // Axis
// Draw main axis label and actions assigned to the full axis
if(selectedController.type != ControllerType.Keyboard) { // don't draw this for keyboards since keys can only be assigned to the +/- axes anyway
GUILayout.BeginHorizontal();
GUILayout.Label(name, GUILayout.Width(labelWidth));
DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Full, selectedController, selectedMap); // Add assignment button
// Write out assigned elements
foreach(ActionElementMap elementMap in selectedMap.AllMaps) {
if(elementMap.actionId != action.id) continue;
if(elementMap.elementType == ControllerElementType.Button) continue; // skip buttons, will handle below
if(elementMap.axisType == AxisType.Split) continue; // skip split axes, will handle below
DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Full, selectedController, selectedMap, elementMap);
DrawInvertButton(selectedPlayer.id, action, Pole.Positive, selectedController, selectedMap, elementMap);
}
GUILayout.EndHorizontal();
}
// Positive action
string positiveName = action.positiveDescriptiveName != string.Empty ? action.positiveDescriptiveName : action.descriptiveName + " +";
GUILayout.BeginHorizontal();
GUILayout.Label(positiveName, GUILayout.Width(labelWidth));
DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap); // Add assignment button
// Write out assigned elements
foreach(ActionElementMap elementMap in selectedMap.AllMaps) {
if(elementMap.actionId != action.id) continue;
if(elementMap.axisContribution != Pole.Positive) continue; // axis contribution is incorrect, skip
if(elementMap.axisType == AxisType.Normal) continue; // normal axes handled above
DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Positive, selectedController, selectedMap, elementMap);
}
GUILayout.EndHorizontal();
// Negative action
string negativeName = action.negativeDescriptiveName != string.Empty ? action.negativeDescriptiveName : action.descriptiveName + " -";
GUILayout.BeginHorizontal();
GUILayout.Label(negativeName, GUILayout.Width(labelWidth));
DrawAddActionMapButton(selectedPlayer.id, action, AxisRange.Negative, selectedController, selectedMap); // Add assignment button
// Write out assigned elements
foreach(ActionElementMap elementMap in selectedMap.AllMaps) {
if(elementMap.actionId != action.id) continue;
if(elementMap.axisContribution != Pole.Negative) continue; // axis contribution is incorrect, skip
if(elementMap.axisType == AxisType.Normal) continue; // normal axes handled above
DrawActionAssignmentButton(selectedPlayer.id, action, AxisRange.Negative, selectedController, selectedMap, elementMap);
}
GUILayout.EndHorizontal();
}
}
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled; // restore GUI state
}
#endregion
#region Buttons
private void DrawActionAssignmentButton(int playerId, InputAction action, AxisRange actionRange, ControllerSelection controller, ControllerMap controllerMap, ActionElementMap elementMap) {
if(GUILayout.Button(elementMap.elementIdentifierName, GUILayout.ExpandWidth(false), GUILayout.MinWidth(30.0f))) {
InputMapper.Context context = new InputMapper.Context() {
actionId = action.id,
actionRange = actionRange,
controllerMap = controllerMap,
actionElementMapToReplace = elementMap
};
EnqueueAction(new ElementAssignmentChange(ElementAssignmentChangeType.ReassignOrRemove, context));
startListening = true;
}
GUILayout.Space(4);
}
private void DrawInvertButton(int playerId, InputAction action, Pole actionAxisContribution, ControllerSelection controller, ControllerMap controllerMap, ActionElementMap elementMap) {
bool value = elementMap.invert;
bool newValue = GUILayout.Toggle(value, "Invert", GUILayout.ExpandWidth(false));
if(newValue != value) {
elementMap.invert = newValue;
}
GUILayout.Space(10);
}
private void DrawAddActionMapButton(int playerId, InputAction action, AxisRange actionRange, ControllerSelection controller, ControllerMap controllerMap) {
if(GUILayout.Button("Add...", GUILayout.ExpandWidth(false))) {
InputMapper.Context context = new InputMapper.Context() {
actionId = action.id,
actionRange = actionRange,
controllerMap = controllerMap
};
EnqueueAction(new ElementAssignmentChange(ElementAssignmentChangeType.Add, context));
startListening = true;
}
GUILayout.Space(10);
}
#endregion
#region Dialog Boxes
private void ShowDialog() {
dialog.Update();
}
#region Draw Window Functions
private void DrawModalWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
// Buttons
dialog.DrawConfirmButton("Okay");
GUILayout.FlexibleSpace();
dialog.DrawCancelButton();
GUILayout.EndHorizontal();
}
private void DrawModalWindow_OkayOnly(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
// Buttons
dialog.DrawConfirmButton("Okay");
GUILayout.EndHorizontal();
}
private void DrawElementAssignmentWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue
if(entry == null) {
dialog.Cancel();
return;
}
float time;
// Do not start until dialog is ready
if(!dialog.busy) {
// Start the listener
if(startListening && inputMapper.status == InputMapper.Status.Idle) {
inputMapper.Start(entry.context);
startListening = false;
}
// Check for conflicts
if(conflictFoundEventData != null) { // a conflict is pending
dialog.Confirm();
return;
}
time = inputMapper.timeRemaining;
// Check for timeout
if(time == 0f) {
dialog.Cancel();
return;
}
} else {
time = inputMapper.options.timeout;
}
// Show the cancel timer
GUILayout.Label("Assignment will be canceled in " + ((int)Mathf.Ceil(time)).ToString() + "...", style_wordWrap);
}
private void DrawElementAssignmentProtectedConflictWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue
if(entry == null) {
dialog.Cancel();
return;
}
// Draw Buttons
GUILayout.BeginHorizontal();
dialog.DrawConfirmButton(UserResponse.Custom1, "Add");
GUILayout.FlexibleSpace();
dialog.DrawCancelButton();
GUILayout.EndHorizontal();
}
private void DrawElementAssignmentNormalConflictWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
ElementAssignmentChange entry = actionQueue.Peek() as ElementAssignmentChange; // get item from queue
if(entry == null) {
dialog.Cancel();
return;
}
// Draw Buttons
GUILayout.BeginHorizontal();
dialog.DrawConfirmButton(UserResponse.Confirm, "Replace");
GUILayout.FlexibleSpace();
dialog.DrawConfirmButton(UserResponse.Custom1, "Add");
GUILayout.FlexibleSpace();
dialog.DrawCancelButton();
GUILayout.EndHorizontal();
}
private void DrawReassignOrRemoveElementAssignmentWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
// Buttons
dialog.DrawConfirmButton("Reassign");
GUILayout.FlexibleSpace();
dialog.DrawCancelButton("Remove");
GUILayout.EndHorizontal();
}
private void DrawFallbackJoystickIdentificationWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
FallbackJoystickIdentification entry = actionQueue.Peek() as FallbackJoystickIdentification;
if(entry == null) {
dialog.Cancel();
return;
}
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.Label("Press any button or axis on \"" + entry.joystickName + "\" now.", style_wordWrap);
GUILayout.FlexibleSpace();
if(GUILayout.Button("Skip")) {
dialog.Cancel();
return;
}
// Do not allow assignment until dialog is ready
if(dialog.busy) return;
// Remap the joystick input source
bool success = ReInput.controllers.SetUnityJoystickIdFromAnyButtonOrAxisPress(entry.joystickId, 0.8f, false);
if(!success) return;
// Finish
dialog.Confirm();
}
private void DrawCalibrationWindow(string title, string message) {
if(!dialog.enabled) return; // prevent this from running after dialog is closed
Calibration entry = actionQueue.Peek() as Calibration;
if(entry == null) {
dialog.Cancel();
return;
}
GUILayout.Space(5);
// Message
GUILayout.Label(message, style_wordWrap);
GUILayout.Space(20);
GUILayout.BeginHorizontal();
bool origGUIEnabled = GUI.enabled;
// Controller element selection
GUILayout.BeginVertical(GUILayout.Width(200));
// Create a scroll view for the axis list in case using the default controller map which has a lot of axes
calibrateScrollPos = GUILayout.BeginScrollView(calibrateScrollPos);
if(entry.recording) GUI.enabled = false; // don't allow switching while recording min/max
IList<ControllerElementIdentifier> axisIdentifiers = entry.joystick.AxisElementIdentifiers;
for(int i = 0; i < axisIdentifiers.Count; i++) {
ControllerElementIdentifier identifier = axisIdentifiers[i];
bool isSelected = entry.selectedElementIdentifierId == identifier.id;
bool newValue = GUILayout.Toggle(isSelected, identifier.name, "Button", GUILayout.ExpandWidth(false));
if(isSelected != newValue) {
entry.selectedElementIdentifierId = identifier.id; // store the selection index
}
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore gui
GUILayout.EndScrollView();
GUILayout.EndVertical();
// Selected object information and controls
GUILayout.BeginVertical(GUILayout.Width(200));
if(entry.selectedElementIdentifierId >= 0) {
float axisValue = entry.joystick.GetAxisRawById(entry.selectedElementIdentifierId);
GUILayout.Label("Raw Value: " + axisValue.ToString());
// Get the axis index from the element identifier id
int axisIndex = entry.joystick.GetAxisIndexById(entry.selectedElementIdentifierId);
AxisCalibration axis = entry.calibrationMap.GetAxis(axisIndex); // get the axis calibration
// Show current axis information
GUILayout.Label("Calibrated Value: " + entry.joystick.GetAxisById(entry.selectedElementIdentifierId));
GUILayout.Label("Zero: " + axis.calibratedZero);
GUILayout.Label("Min: " + axis.calibratedMin);
GUILayout.Label("Max: " + axis.calibratedMax);
GUILayout.Label("Dead Zone: " + axis.deadZone);
GUILayout.Space(15);
// Enabled -- allows user to disable an axis entirely if its giving them problems
bool newEnabled = GUILayout.Toggle(axis.enabled, "Enabled", "Button", GUILayout.ExpandWidth(false));
if(axis.enabled != newEnabled) {
axis.enabled = newEnabled;
}
GUILayout.Space(10);
// Records Min/Max
bool newRecording = GUILayout.Toggle(entry.recording, "Record Min/Max", "Button", GUILayout.ExpandWidth(false));
if(newRecording != entry.recording) {
if(newRecording) { // just started recording
// Clear previous calibration so we can record min max from this session only
axis.calibratedMax = 0.0f;
axis.calibratedMin = 0.0f;
}
entry.recording = newRecording;
}
if(entry.recording) {
axis.calibratedMin = Mathf.Min(axis.calibratedMin, axisValue, axis.calibratedMin);
axis.calibratedMax = Mathf.Max(axis.calibratedMax, axisValue, axis.calibratedMax);
GUI.enabled = false;
}
// Set Zero state
if(GUILayout.Button("Set Zero", GUILayout.ExpandWidth(false))) {
axis.calibratedZero = axisValue;
}
// Set Dead Zone
if(GUILayout.Button("Set Dead Zone", GUILayout.ExpandWidth(false))) {
axis.deadZone = axisValue;
}
// Invert
bool newInvert = GUILayout.Toggle(axis.invert, "Invert", "Button", GUILayout.ExpandWidth(false));
if(axis.invert != newInvert) {
axis.invert = newInvert;
}
GUILayout.Space(10);
if(GUILayout.Button("Reset", GUILayout.ExpandWidth(false))) {
axis.Reset();
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled;
} else {
GUILayout.Label("Select an axis to begin.");
}
GUILayout.EndVertical();
GUILayout.EndHorizontal();
// Close Button
GUILayout.FlexibleSpace();
if(entry.recording) GUI.enabled = false;
if(GUILayout.Button("Close")) {
calibrateScrollPos = new Vector2(); // clear the scroll view position
dialog.Confirm();
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled;
}
#endregion
#region Result Callbacks
private void DialogResultCallback(int queueActionId, UserResponse response) {
foreach(QueueEntry entry in actionQueue) { // find the right one and cancel or confirm it
if(entry.id != queueActionId) continue;
if(response != UserResponse.Cancel) entry.Confirm(response); // mark the entry as confirmed and record user response
else entry.Cancel(); // mark the entry as canceled
break;
}
}
#endregion
#region Misc
private Rect GetScreenCenteredRect(float width, float height) {
return new Rect(
(float)(Screen.width * 0.5f - width * 0.5f),
(float)(Screen.height * 0.5 - height * 0.5f),
width,
height
);
}
#endregion
#endregion
#region Action Queue
private void EnqueueAction(QueueEntry entry) {
if(entry == null) return;
busy = true;
GUI.enabled = false; // disable control on everything until next cycle
actionQueue.Enqueue(entry);
}
private void ProcessQueue() {
if(dialog.enabled) return; // dialog is open, do not process queue
if(busy || actionQueue.Count == 0) return;
while(actionQueue.Count > 0) {
QueueEntry queueEntry = actionQueue.Peek(); // get next item from queue
bool goNext = false;
// Process different types of actions
switch(queueEntry.queueActionType) {
case QueueActionType.JoystickAssignment:
goNext = ProcessJoystickAssignmentChange((JoystickAssignmentChange)queueEntry);
break;
case QueueActionType.ElementAssignment:
goNext = ProcessElementAssignmentChange((ElementAssignmentChange)queueEntry);
break;
case QueueActionType.FallbackJoystickIdentification:
goNext = ProcessFallbackJoystickIdentification((FallbackJoystickIdentification)queueEntry);
break;
case QueueActionType.Calibrate:
goNext = ProcessCalibration((Calibration)queueEntry);
break;
}
// Quit processing the queue if we opened a modal
if(!goNext) break;
// Remove item from queue since we're done with it
actionQueue.Dequeue();
}
}
private bool ProcessJoystickAssignmentChange(JoystickAssignmentChange entry) {
// Handle user cancelation
if(entry.state == QueueEntry.State.Canceled) { // action was canceled
return true;
}
Player player = ReInput.players.GetPlayer(entry.playerId);
if(player == null) return true;
if(!entry.assign) { // deassign joystick
player.controllers.RemoveController(ControllerType.Joystick, entry.joystickId);
ControllerSelectionChanged();
return true;
}
// Assign joystick
if(player.controllers.ContainsController(ControllerType.Joystick, entry.joystickId)) return true; // same player, nothing to change
bool alreadyAssigned = ReInput.controllers.IsJoystickAssigned(entry.joystickId);
if(!alreadyAssigned || entry.state == QueueEntry.State.Confirmed) { // not assigned or user confirmed the action already, do it
player.controllers.AddController(ControllerType.Joystick, entry.joystickId, true);
ControllerSelectionChanged();
return true;
}
// Create dialog and start waiting for user confirmation
dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties {
title = "Joystick Reassignment",
message = "This joystick is already assigned to another player. Do you want to reassign this joystick to " + player.descriptiveName + "?",
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawModalWindow
},
DialogResultCallback);
return false; // don't process anything more in this queue
}
private bool ProcessElementAssignmentChange(ElementAssignmentChange entry) {
switch(entry.changeType) {
case ElementAssignmentChangeType.ReassignOrRemove:
return ProcessRemoveOrReassignElementAssignment(entry);
case ElementAssignmentChangeType.Remove:
return ProcessRemoveElementAssignment(entry);
case ElementAssignmentChangeType.Add:
case ElementAssignmentChangeType.Replace:
return ProcessAddOrReplaceElementAssignment(entry);
case ElementAssignmentChangeType.ConflictCheck:
return ProcessElementAssignmentConflictCheck(entry);
default:
throw new System.NotImplementedException();
}
}
private bool ProcessRemoveOrReassignElementAssignment(ElementAssignmentChange entry) {
if(entry.context.controllerMap == null) return true;
if(entry.state == QueueEntry.State.Canceled) { // delete entry
// Enqueue a new action to delete the entry
ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // copy the entry
newEntry.changeType = ElementAssignmentChangeType.Remove; // change the type to Remove
actionQueue.Enqueue(newEntry); // enqueue the new entry
return true;
}
// Check for user confirmation
if(entry.state == QueueEntry.State.Confirmed) { // reassign entry
// Enqueue a new action to reassign the entry
ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // copy the entry
newEntry.changeType = ElementAssignmentChangeType.Replace; // change the type to Replace
actionQueue.Enqueue(newEntry); // enqueue the new entry
return true;
}
// Create dialog and start waiting for user assignment
dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties {
title = "Reassign or Remove",
message = "Do you want to reassign or remove this assignment?",
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawReassignOrRemoveElementAssignmentWindow
},
DialogResultCallback);
return false;
}
private bool ProcessRemoveElementAssignment(ElementAssignmentChange entry) {
if(entry.context.controllerMap == null) return true;
if(entry.state == QueueEntry.State.Canceled) return true; // user canceled
// Delete element
if(entry.state == QueueEntry.State.Confirmed) { // user confirmed, delete it
entry.context.controllerMap.DeleteElementMap(entry.context.actionElementMapToReplace.id);
return true;
}
// Create dialog and start waiting for user confirmation
dialog.StartModal(entry.id, DialogHelper.DialogType.DeleteAssignmentConfirmation, new WindowProperties {
title = "Remove Assignment",
message = "Are you sure you want to remove this assignment?",
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawModalWindow
},
DialogResultCallback);
return false; // don't process anything more in this queue
}
private bool ProcessAddOrReplaceElementAssignment(ElementAssignmentChange entry) {
if(entry.state == QueueEntry.State.Canceled) {
inputMapper.Stop();
return true; // user canceled
}
// Check for user confirmation
if(entry.state == QueueEntry.State.Confirmed) { // the action assignment has been confirmed
if(Event.current.type != EventType.Layout) return false; // only make changes in layout to avoid GUI errors when new controls appear
// Handle conflicts
if(conflictFoundEventData != null) { // we had conflicts
// Enqueue a conflict check
ElementAssignmentChange newEntry = new ElementAssignmentChange(entry); // clone the entry
newEntry.changeType = ElementAssignmentChangeType.ConflictCheck; // set the new type to check for conflicts
actionQueue.Enqueue(newEntry); // enqueue the new entry
}
return true; // finished
}
// Customize the message for different controller types and different platforms
string message;
if(entry.context.controllerMap.controllerType == ControllerType.Keyboard) {
#if UNITY_5_4_OR_NEWER
bool isOSX = Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer;
#else
bool isOSX = Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.OSXWebPlayer;
#endif
if(isOSX) {
message = "Press any key to assign it to this action. You may also use the modifier keys Command, Control, Alt, and Shift. If you wish to assign a modifier key itself to this action, press and hold the key for 1 second.";
} else {
message = "Press any key to assign it to this action. You may also use the modifier keys Control, Alt, and Shift. If you wish to assign a modifier key itself to this action, press and hold the key for 1 second.";
}
// Editor modifier key disclaimer
if(Application.isEditor) {
message += "\n\nNOTE: Some modifier key combinations will not work in the Unity Editor, but they will work in a game build.";
}
} else if(entry.context.controllerMap.controllerType == ControllerType.Mouse) {
message = "Press any mouse button or axis to assign it to this action.\n\nTo assign mouse movement axes, move the mouse quickly in the direction you want mapped to the action. Slow movements will be ignored.";
} else {
message = "Press any button or axis to assign it to this action.";
}
// Create dialog and start waiting for user assignment
dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties {
title = "Assign",
message = message,
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawElementAssignmentWindow
},
DialogResultCallback);
return false;
}
private bool ProcessElementAssignmentConflictCheck(ElementAssignmentChange entry) {
if(entry.context.controllerMap == null) return true;
if(entry.state == QueueEntry.State.Canceled) {
inputMapper.Stop();
return true; // user canceled
}
if(conflictFoundEventData == null) return true; // error
// Check for user confirmation
if(entry.state == QueueEntry.State.Confirmed) {
if(entry.response == UserResponse.Confirm) { // remove and add
conflictFoundEventData.responseCallback(InputMapper.ConflictResponse.Replace);
} else if(entry.response == UserResponse.Custom1) { // add without removing
conflictFoundEventData.responseCallback(InputMapper.ConflictResponse.Add);
} else throw new System.NotImplementedException();
return true; // finished
}
// Open a different dialog depending on if a protected conflict was found
if(conflictFoundEventData.isProtected) {
string message = conflictFoundEventData.assignment.elementDisplayName + " is already in use and is protected from reassignment. You cannot remove the protected assignment, but you can still assign the action to this element. If you do so, the element will trigger multiple actions when activated.";
// Create dialog and start waiting for user assignment
dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties {
title = "Assignment Conflict",
message = message,
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawElementAssignmentProtectedConflictWindow
},
DialogResultCallback);
} else {
string message = conflictFoundEventData.assignment.elementDisplayName + " is already in use. You may replace the other conflicting assignments, add this assignment anyway which will leave multiple actions assigned to this element, or cancel this assignment.";
// Create dialog and start waiting for user assignment
dialog.StartModal(entry.id, DialogHelper.DialogType.AssignElement, new WindowProperties {
title = "Assignment Conflict",
message = message,
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawElementAssignmentNormalConflictWindow
},
DialogResultCallback);
}
return false;
}
private bool ProcessFallbackJoystickIdentification(FallbackJoystickIdentification entry) {
// Handle user cancelation
if(entry.state == QueueEntry.State.Canceled) { // action was canceled
return true;
}
// Identify joystick
if(entry.state == QueueEntry.State.Confirmed) {
// nothing to do, done
return true;
}
// Create dialog and start waiting for user confirmation
dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties {
title = "Joystick Identification Required",
message = "A joystick has been attached or removed. You will need to identify each joystick by pressing a button on the controller listed below:",
rect = GetScreenCenteredRect(defaultModalWidth, defaultModalHeight),
windowDrawDelegate = DrawFallbackJoystickIdentificationWindow
},
DialogResultCallback,
1.0f); // add a longer delay after the dialog opens to prevent one joystick press from being used for subsequent joysticks if held for a short time
return false; // don't process anything more in this queue
}
private bool ProcessCalibration(Calibration entry) {
// Handle user cancelation
if(entry.state == QueueEntry.State.Canceled) { // action was canceled
return true;
}
if(entry.state == QueueEntry.State.Confirmed) {
return true;
}
// Create dialog and start waiting for user confirmation
dialog.StartModal(entry.id, DialogHelper.DialogType.JoystickConflict, new WindowProperties {
title = "Calibrate Controller",
message = "Select an axis to calibrate on the " + entry.joystick.name + ".",
rect = GetScreenCenteredRect(450, 480),
windowDrawDelegate = DrawCalibrationWindow
},
DialogResultCallback);
return false; // don't process anything more in this queue
}
#endregion
#region Selection Chaging
private void PlayerSelectionChanged() {
ClearControllerSelection();
}
private void ControllerSelectionChanged() {
ClearMapSelection();
}
private void ClearControllerSelection() {
selectedController.Clear(); // reset the device selection because joystick list will have changed
ClearMapSelection();
}
#endregion
#region Clear
private void ClearMapSelection() {
selectedMapCategoryId = -1; // clear map cat selection
selectedMap = null;
}
private void ResetAll() {
ClearWorkingVars();
initialized = false;
showMenu = false;
}
private void ClearWorkingVars() {
selectedPlayer = null;
ClearMapSelection();
selectedController.Clear();
actionScrollPos = new Vector2();
dialog.FullReset();
actionQueue.Clear();
busy = false;
startListening = false;
conflictFoundEventData = null;
inputMapper.Stop();
}
#endregion
#region GUI State
private void SetGUIStateStart() {
guiState = true;
if(busy) guiState = false;
pageGUIState = guiState && !busy && !dialog.enabled && !dialog.busy; // enable page gui only if not busy and not in dialog mode
if(GUI.enabled != guiState) GUI.enabled = guiState;
}
private void SetGUIStateEnd() {
// always enable GUI again before exiting
guiState = true;
if(!GUI.enabled) GUI.enabled = guiState;
}
#endregion
#region Joystick Connection Callbacks
private void JoystickConnected(ControllerStatusChangedEventArgs args) {
// Reload maps if a joystick is connected
if(ReInput.controllers.IsControllerAssigned(args.controllerType, args.controllerId)) {
// Load the maps for the player(s) that are assigned this joystick
foreach(Player player in ReInput.players.AllPlayers) {
if(player.controllers.ContainsController(args.controllerType, args.controllerId)) {
ReInput.userDataStore.LoadControllerData(player.id, args.controllerType, args.controllerId);
}
}
} else {
// Just load the general joystick save data
ReInput.userDataStore.LoadControllerData(args.controllerType, args.controllerId);
}
// Always force reidentification of all joysticks when a joystick is added or removed when using Unity input on a platform that requires manual identification
if(ReInput.unityJoystickIdentificationRequired) IdentifyAllJoysticks();
}
private void JoystickPreDisconnect(ControllerStatusChangedEventArgs args) {
// Check if the current editing controller was just disconnected and deselect it
if(selectedController.hasSelection && args.controllerType == selectedController.type && args.controllerId == selectedController.id) {
ClearControllerSelection(); // joystick was disconnected
}
// Save the user maps before the joystick is disconnected if in the menu since user may have changed something
if(showMenu) {
if(ReInput.controllers.IsControllerAssigned(args.controllerType, args.controllerId)) {
foreach(Player player in ReInput.players.AllPlayers) {
if(!player.controllers.ContainsController(args.controllerType, args.controllerId)) continue;
ReInput.userDataStore.SaveControllerData(player.id, args.controllerType, args.controllerId);
}
} else {
ReInput.userDataStore.SaveControllerData(args.controllerType, args.controllerId);
}
}
}
private void JoystickDisconnected(ControllerStatusChangedEventArgs args) {
// Close dialogs and clear queue if a joystick is disconnected
if(showMenu) ClearWorkingVars();
// Always force reidentification of all joysticks when a joystick is added or removed when using Unity input
if(ReInput.unityJoystickIdentificationRequired) IdentifyAllJoysticks();
}
#endregion
#region Mapping Listener Event Handlers
private void OnConflictFound(InputMapper.ConflictFoundEventData data) {
this.conflictFoundEventData = data;
}
private void OnStopped(InputMapper.StoppedEventData data) {
this.conflictFoundEventData = null;
}
#endregion
#region Fallback Methods
public void IdentifyAllJoysticks() {
// Check if there are any joysticks
if(ReInput.controllers.joystickCount == 0) return; // no joysticks, nothing to do
// Clear all vars first which will clear dialogs and queues
ClearWorkingVars();
// Open the menu if its closed
Open();
// Enqueue the joysticks up for identification
foreach(Joystick joystick in ReInput.controllers.Joysticks) {
actionQueue.Enqueue(new FallbackJoystickIdentification(joystick.id, joystick.name)); // enqueue each joystick for identification
}
}
#endregion
#region Editor Methods
protected void CheckRecompile() {
#if UNITY_EDITOR
// Destroy system if recompiling
if(UnityEditor.EditorApplication.isCompiling) { // editor is recompiling
if(!isCompiling) { // this is the first cycle of recompile
ResetAll();
isCompiling = true;
}
GUILayout.Window(0, GetScreenCenteredRect(300, 100), RecompileWindow, new GUIContent("Scripts are Compiling"));
return;
}
// Check for end of compile
if(isCompiling) { // compiling is done
isCompiling = false;
Initialize();
}
#endif
}
private void RecompileWindow(int windowId) {
#if UNITY_EDITOR
GUILayout.FlexibleSpace();
GUILayout.Label("Please wait while script compilation finishes...");
GUILayout.FlexibleSpace();
#endif
}
#endregion
#region Classes
private class ControllerSelection {
private int _id;
private int _idPrev;
private ControllerType _type;
private ControllerType _typePrev;
public ControllerSelection() {
Clear();
}
public int id {
get {
return _id;
}
set {
_idPrev = _id;
_id = value;
}
}
public ControllerType type {
get {
return _type;
}
set {
_typePrev = _type;
_type = value;
}
}
public int idPrev { get { return _idPrev; } }
public ControllerType typePrev { get { return _typePrev; } }
public bool hasSelection { get { return _id >= 0; } }
public void Set(int id, ControllerType type) {
this.id = id;
this.type = type;
}
public void Clear() {
_id = -1;
_idPrev = -1;
_type = ControllerType.Joystick;
_typePrev = ControllerType.Joystick;
}
}
private class DialogHelper {
private const float openBusyDelay = 0.25f; // a small delay after opening the window that prevents assignment input for a short time after window opens
private const float closeBusyDelay = 0.1f; // a small after closing the window that the GUI will still be busy to prevent button clickthrough
private DialogType _type;
private bool _enabled;
private float _busyTime;
private bool _busyTimerRunning;
private float busyTimer { get { if(!_busyTimerRunning) return 0.0f; return _busyTime - Time.realtimeSinceStartup; } }
public bool enabled {
get {
return _enabled;
}
set {
if(value) {
if(_type == DialogType.None) return; // cannot enable, no type set
StateChanged(openBusyDelay);
} else {
_enabled = value;
_type = DialogType.None;
StateChanged(closeBusyDelay);
}
}
}
public DialogType type {
get {
if(!_enabled) return DialogType.None;
return _type;
}
set {
if(value == DialogType.None) {
_enabled = false;
StateChanged(closeBusyDelay);
} else {
_enabled = true;
StateChanged(openBusyDelay);
}
_type = value;
}
}
public bool busy { get { return _busyTimerRunning; } }
private Action<int> drawWindowDelegate;
private GUI.WindowFunction drawWindowFunction;
private WindowProperties windowProperties;
private int currentActionId;
private Action<int, UserResponse> resultCallback;
public DialogHelper() {
drawWindowDelegate = DrawWindow;
drawWindowFunction = new GUI.WindowFunction(drawWindowDelegate);
}
public void StartModal(int queueActionId, DialogType type, WindowProperties windowProperties, Action<int, UserResponse> resultCallback) {
StartModal(queueActionId, type, windowProperties, resultCallback, -1.0f);
}
public void StartModal(int queueActionId, DialogType type, WindowProperties windowProperties, Action<int, UserResponse> resultCallback, float openBusyDelay) {
currentActionId = queueActionId;
this.windowProperties = windowProperties;
this.type = type;
this.resultCallback = resultCallback;
if(openBusyDelay >= 0.0f) StateChanged(openBusyDelay); // override with user defined open busy delay
}
public void Update() {
Draw();
UpdateTimers();
}
public void Draw() {
if(!_enabled) return;
bool origGuiEnabled = GUI.enabled;
GUI.enabled = true;
GUILayout.Window(windowProperties.windowId, windowProperties.rect, drawWindowFunction, windowProperties.title);
GUI.FocusWindow(windowProperties.windowId);
if(GUI.enabled != origGuiEnabled) GUI.enabled = origGuiEnabled;
}
public void DrawConfirmButton() {
DrawConfirmButton("Confirm");
}
public void DrawConfirmButton(string title) {
bool origGUIEnabled = GUI.enabled; // store original gui state
if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though
if(GUILayout.Button(title)) {
Confirm(UserResponse.Confirm);
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI
}
public void DrawConfirmButton(UserResponse response) {
DrawConfirmButton(response, "Confirm");
}
public void DrawConfirmButton(UserResponse response, string title) {
bool origGUIEnabled = GUI.enabled; // store original gui state
if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though
if(GUILayout.Button(title)) {
Confirm(response);
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI
}
public void DrawCancelButton() {
DrawCancelButton("Cancel");
}
public void DrawCancelButton(string title) {
bool origGUIEnabled = GUI.enabled; // store original gui state
if(busy) GUI.enabled = false; // disable GUI if dialog is busy to prevent click though
if(GUILayout.Button(title)) {
Cancel();
}
if(GUI.enabled != origGUIEnabled) GUI.enabled = origGUIEnabled; // restore GUI
}
public void Confirm() {
Confirm(UserResponse.Confirm);
}
public void Confirm(UserResponse response) {
resultCallback(currentActionId, response);
Close();
}
public void Cancel() {
resultCallback(currentActionId, UserResponse.Cancel);
Close();
}
private void DrawWindow(int windowId) {
windowProperties.windowDrawDelegate(windowProperties.title, windowProperties.message);
}
private void UpdateTimers() {
if(_busyTimerRunning) {
if(busyTimer <= 0.0f) _busyTimerRunning = false;
}
}
private void StartBusyTimer(float time) {
_busyTime = time + Time.realtimeSinceStartup;
_busyTimerRunning = true;
}
private void Close() {
Reset();
StateChanged(closeBusyDelay);
}
private void StateChanged(float delay) {
StartBusyTimer(delay);
}
private void Reset() {
_enabled = false;
_type = DialogType.None;
currentActionId = -1;
resultCallback = null;
}
private void ResetTimers() {
_busyTimerRunning = false;
}
public void FullReset() {
Reset();
ResetTimers();
}
// Enums
public enum DialogType {
None = 0,
JoystickConflict = 1,
ElementConflict = 2,
KeyConflict = 3,
DeleteAssignmentConfirmation = 10,
AssignElement = 11
}
}
private abstract class QueueEntry {
public int id { get; protected set; }
public QueueActionType queueActionType { get; protected set; }
public State state { get; protected set; }
public UserResponse response { get; protected set; }
private static int uidCounter;
protected static int nextId {
get {
int id = uidCounter;
uidCounter += 1;
return id;
}
}
public QueueEntry(QueueActionType queueActionType) {
id = nextId;
this.queueActionType = queueActionType;
}
public void Confirm(UserResponse response) {
state = State.Confirmed;
this.response = response;
}
public void Cancel() {
state = State.Canceled;
}
public enum State {
Waiting = 0,
Confirmed = 1,
Canceled = 2
}
}
private class JoystickAssignmentChange : QueueEntry {
public int playerId { get; private set; }
public int joystickId { get; private set; }
public bool assign { get; private set; }
public JoystickAssignmentChange(
int newPlayerId,
int joystickId,
bool assign
)
: base(QueueActionType.JoystickAssignment) {
this.playerId = newPlayerId;
this.joystickId = joystickId;
this.assign = assign;
}
}
private class ElementAssignmentChange : QueueEntry {
public ElementAssignmentChangeType changeType { get; set; }
public InputMapper.Context context { get; private set; }
public ElementAssignmentChange(ElementAssignmentChangeType changeType, InputMapper.Context context)
: base(QueueActionType.ElementAssignment) {
this.changeType = changeType;
this.context = context;
}
public ElementAssignmentChange(ElementAssignmentChange other)
: this(other.changeType, other.context.Clone()) {
}
}
private class FallbackJoystickIdentification : QueueEntry {
public int joystickId { get; private set; }
public string joystickName { get; private set; }
public FallbackJoystickIdentification(
int joystickId,
string joystickName
)
: base(QueueActionType.FallbackJoystickIdentification) {
this.joystickId = joystickId;
this.joystickName = joystickName;
}
}
private class Calibration : QueueEntry {
public Player player { get; private set; }
public ControllerType controllerType { get; private set; }
public Joystick joystick { get; private set; }
public CalibrationMap calibrationMap { get; private set; }
public int selectedElementIdentifierId;
public bool recording;
public Calibration(
Player player,
Joystick joystick,
CalibrationMap calibrationMap
)
: base(QueueActionType.Calibrate) {
this.player = player;
this.joystick = joystick;
this.calibrationMap = calibrationMap;
selectedElementIdentifierId = -1;
}
}
private struct WindowProperties {
public int windowId;
public Rect rect;
public Action<string, string> windowDrawDelegate;
public string title;
public string message;
}
#endregion
#region Enums
private enum QueueActionType {
None = 0,
JoystickAssignment = 1,
ElementAssignment = 2,
FallbackJoystickIdentification = 3,
Calibrate = 4
}
private enum ElementAssignmentChangeType {
Add = 0,
Replace = 1,
Remove = 2,
ReassignOrRemove = 3,
ConflictCheck = 4
}
public enum UserResponse {
Confirm = 0,
Cancel = 1,
Custom1 = 2,
Custom2 = 3
}
#endregion
}
}