using System;
using System.ComponentModel;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Scripting;
#if UNITY_EDITOR
using UnityEngine.InputSystem.Editor;
using UnityEngine.UIElements;
#endif
namespace UnityEngine.InputSystem.Interactions
{
///
/// Performs the action if the control is pressed and held for at least the
/// set duration (which defaults to ).
///
///
/// The action is started when the control is pressed. If the control is released before the
/// set , the action is canceled. As soon as the hold time is reached,
/// the action performs. The action then stays performed until the control is released, at
/// which point the action cancels.
///
///
///
/// // Action that requires A button on gamepad to be held for half a second.
/// var action = new InputAction(binding: "<Gamepad>/buttonSouth", interactions: "hold(duration=0.5)");
///
///
///
[DisplayName("Hold")]
public class HoldInteraction : IInputInteraction
{
///
/// Duration in seconds that the control must be pressed for the hold to register.
///
///
/// If this is less than or equal to 0 (the default), is used.
///
/// Duration is expressed in real time and measured against the timestamps of input events
/// () not against game time ().
///
public float duration;
///
/// Magnitude threshold that must be crossed by an actuated control for the control to
/// be considered pressed.
///
///
/// If this is less than or equal to 0 (the default), is used instead.
///
///
public float pressPoint;
private float durationOrDefault => duration > 0.0 ? duration : InputSystem.settings.defaultHoldTime;
private float pressPointOrDefault => pressPoint > 0.0 ? pressPoint : ButtonControl.s_GlobalDefaultButtonPressPoint;
private double m_TimePressed;
///
public void Process(ref InputInteractionContext context)
{
if (context.timerHasExpired)
{
context.PerformedAndStayPerformed();
return;
}
switch (context.phase)
{
case InputActionPhase.Waiting:
if (context.ControlIsActuated(pressPointOrDefault))
{
m_TimePressed = context.time;
context.Started();
context.SetTimeout(durationOrDefault);
}
break;
case InputActionPhase.Started:
// If we've reached our hold time threshold, perform the hold.
// We do this regardless of what state the control changed to.
if (context.time - m_TimePressed >= durationOrDefault)
{
context.PerformedAndStayPerformed();
}
if (!context.ControlIsActuated())
{
// Control is no longer actuated so we're done.
context.Canceled();
}
break;
case InputActionPhase.Performed:
if (!context.ControlIsActuated(pressPointOrDefault))
context.Canceled();
break;
}
}
///
public void Reset()
{
m_TimePressed = 0;
}
}
#if UNITY_EDITOR
///
/// UI that is displayed when editing in the editor.
///
internal class HoldInteractionEditor : InputParameterEditor
{
protected override void OnEnable()
{
m_PressPointSetting.Initialize("Press Point",
"Float value that an axis control has to cross for it to be considered pressed.",
"Default Button Press Point",
() => target.pressPoint, v => target.pressPoint = v, () => ButtonControl.s_GlobalDefaultButtonPressPoint);
m_DurationSetting.Initialize("Hold Time",
"Time (in seconds) that a control has to be held in order for it to register as a hold.",
"Default Hold Time",
() => target.duration, x => target.duration = x, () => InputSystem.settings.defaultHoldTime);
}
public override void OnGUI()
{
m_PressPointSetting.OnGUI();
m_DurationSetting.OnGUI();
}
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
{
m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback);
m_DurationSetting.OnDrawVisualElements(root, onChangedCallback);
}
#endif
private CustomOrDefaultSetting m_PressPointSetting;
private CustomOrDefaultSetting m_DurationSetting;
}
#endif
}