2025-07-08 10:46:31 +00:00
// Copyright (c) Pixel Crushers. All rights reserved.
using System.Collections ;
using System.Collections.Generic ;
using System.Reflection ;
using UnityEngine ;
using UnityEngine.Events ;
namespace PixelCrushers.DialogueSystem
{
[AddComponentMenu("")] // Use wrapper.
public class StandardUIMenuPanel : UIPanel
{
#region Serialized Fields
[Tooltip("(Optional) Main response menu panel.")]
public UnityEngine . UI . Graphic panel ;
[Tooltip("(Optional) Image to show PC portrait during response menu.")]
public UnityEngine . UI . Image pcImage ;
[Tooltip("(Optional) Text element to show PC name during response menu.")]
public UITextField pcName ;
[Tooltip("Set PC Image to actor portrait's native size. Image's Rect Transform can't use Stretch anchors.")]
public bool usePortraitNativeSize = false ;
[Tooltip("(Optional) Slider for timed menus.")]
public UnityEngine . UI . Slider timerSlider ;
[Tooltip("Assign design-time positioned buttons starting with first or last button.")]
public ResponseButtonAlignment buttonAlignment = ResponseButtonAlignment . ToFirst ;
[Tooltip("Show buttons that aren't assigned to any responses. If using a 'dialogue wheel' for example, you'll want to show unused buttons so the entire wheel structure is visible.")]
public bool showUnusedButtons = false ;
[Tooltip("Design-time positioned response buttons. (Optional if Button Template is assigned.)")]
public StandardUIResponseButton [ ] buttons ;
[Tooltip("Template from which to instantiate response buttons. (Optional if using Buttons list above.)")]
public StandardUIResponseButton buttonTemplate ;
[Tooltip("If using Button Template, instantiate buttons under this GameObject.")]
public UnityEngine . UI . Graphic buttonTemplateHolder ;
[Tooltip("(Optional) Scrollbar to use if instantiated button holder is in a scroll rect.")]
public UnityEngine . UI . Scrollbar buttonTemplateScrollbar ;
[Tooltip("(Optional) Component that enables or disables scrollbar as necessary for content.")]
public UIScrollbarEnabler scrollbarEnabler ;
[Tooltip("Reset the scroll bar to this value when preparing response menu. To skip resetting the scrollbar, specify a negative value.")]
public float buttonTemplateScrollbarResetValue = 1 ;
[Tooltip("Automatically set up explicit joystick/keyboard navigation for instantiated template buttons instead of using Automatic navigation.")]
public bool explicitNavigationForTemplateButtons = true ;
[Tooltip("If explicit navigation is enabled, loop around when navigating past end of menu.")]
public bool loopExplicitNavigation = false ;
public UIAutonumberSettings autonumber = new UIAutonumberSettings ( ) ;
[Tooltip("If non-zero, prevent input for this duration in seconds when opening menu.")]
public float blockInputDuration = 0 ;
[Tooltip("During block input duration, keep selected response button in selected visual state.")]
public bool showSelectionWhileInputBlocked = false ;
[Tooltip("Log a warning if a response button text is blank.")]
public bool warnOnEmptyResponseText = false ;
public UnityEvent onContentChanged = new UnityEvent ( ) ;
[Tooltip("When focusing panel, set this animator trigger.")]
public string focusAnimationTrigger = string . Empty ;
[Tooltip("When unfocusing panel, set this animator trigger.")]
public string unfocusAnimationTrigger = string . Empty ;
[Tooltip("Wait for panels within this dialogue UI (not external) to close before showing menu.")]
public bool waitForClose = false ;
/// <summary>
/// Invoked when the subtitle panel gains focus.
/// </summary>
public UnityEvent onFocus = new UnityEvent ( ) ;
/// <summary>
/// Invoked when the subtitle panel loses focus.
/// </summary>
public UnityEvent onUnfocus = new UnityEvent ( ) ;
#endregion
#region Public Properties
[SerializeField, Tooltip("Panel is currently in focused state.")]
private bool m_hasFocus = false ;
public virtual bool hasFocus
{
get { return m_hasFocus ; }
protected set { m_hasFocus = value ; }
}
public override bool waitForShowAnimation { get { return true ; } }
/// <summary>
/// The instantiated buttons. These are only valid during a specific response menu,
/// and only if you're using templates. Each showing of the response menu clears
/// this list and re-populates it with new buttons.
/// </summary>
public List < GameObject > instantiatedButtons { get { return m_instantiatedButtons ; } }
private List < GameObject > m_instantiatedButtons = new List < GameObject > ( ) ;
#endregion
#region Internal Fields
protected List < GameObject > instantiatedButtonPool { get { return m_instantiatedButtonPool ; } }
private List < GameObject > m_instantiatedButtonPool = new List < GameObject > ( ) ;
private string m_processedAutonumberFormat = string . Empty ;
private Coroutine m_scrollbarCoroutine = null ;
protected const float WaitForCloseTimeoutDuration = 8f ;
protected StandardUITimer m_timer = null ;
protected System . Action m_timeoutHandler = null ;
protected CanvasGroup m_mainCanvasGroup = null ;
protected static bool s_isInputDisabled = false ;
private StandardDialogueUI m_dialogueUI = null ;
protected StandardDialogueUI dialogueUI
{
get
{
if ( m_dialogueUI = = null ) m_dialogueUI = GetComponentInParent < StandardDialogueUI > ( ) ;
return m_dialogueUI ? ? DialogueManager . standardDialogueUI ;
}
}
#endregion
#region Initialization
public virtual void Awake ( )
{
Tools . SetGameObjectActive ( buttonTemplate , false ) ;
}
#endregion
#region Show & Hide
protected override void Update ( )
{
if ( s_isInputDisabled )
{
if ( eventSystem ! = null ) eventSystem . SetSelectedGameObject ( null ) ;
}
else
{
base . Update ( ) ;
}
}
public override void CheckFocus ( )
{
if ( s_isInputDisabled ) return ;
base . CheckFocus ( ) ;
}
public virtual void SetPCPortrait ( Sprite portraitSprite , string portraitName )
{
if ( pcImage ! = null )
{
Tools . SetGameObjectActive ( pcImage , portraitSprite ! = null ) ;
pcImage . sprite = portraitSprite ;
if ( usePortraitNativeSize & & portraitSprite ! = null )
{
pcImage . rectTransform . sizeDelta = portraitSprite . packed ?
new Vector2 ( portraitSprite . rect . width , portraitSprite . rect . height ) :
new Vector2 ( portraitSprite . texture . width , portraitSprite . texture . height ) ;
}
}
pcName . text = portraitName ;
}
[System.Obsolete("Use SetPCPortrait(Sprite,string) instead.")]
public virtual void SetPCPortrait ( Texture2D portraitTexture , string portraitName )
{
SetPCPortrait ( UITools . CreateSprite ( portraitTexture ) , portraitName ) ;
}
public virtual void ShowResponses ( Subtitle subtitle , Response [ ] responses , Transform target )
{
if ( waitForClose & & dialogueUI ! = null )
{
if ( dialogueUI . AreAnyPanelsClosing ( ) )
{
DialogueManager . instance . StartCoroutine ( ShowAfterPanelsClose ( subtitle , responses , target ) ) ;
return ;
}
}
CheckForBlankResponses ( responses ) ;
ShowResponsesNow ( subtitle , responses , target ) ;
}
private void CheckForBlankResponses ( Response [ ] responses )
{
if ( ! DialogueDebug . logWarnings ) return ;
if ( responses = = null ) return ;
foreach ( Response response in responses )
{
if ( string . IsNullOrEmpty ( response . formattedText . text ) )
{
Debug . LogWarning ( $"Dialogue System: Response [{response.destinationEntry.conversationID}:{response.destinationEntry.id}] has no text for a response button." ) ;
}
}
}
protected virtual void ShowResponsesNow ( Subtitle subtitle , Response [ ] responses , Transform target )
{
if ( responses = = null | | responses . Length = = 0 )
{
if ( DialogueDebug . logWarnings ) Debug . LogWarning ( "Dialogue System: StandardDialogueUI ShowResponses received an empty list of responses." , this ) ;
return ;
}
ClearResponseButtons ( ) ;
SetResponseButtons ( responses , target ) ;
ActivateUIElements ( ) ;
Open ( ) ;
Focus ( ) ;
RefreshSelectablesList ( ) ;
if ( blockInputDuration > 0 )
{
DisableInput ( ) ;
if ( InputDeviceManager . autoFocus ) SetFocus ( firstSelected ) ;
if ( Mathf . Approximately ( 0 , Time . timeScale ) )
{
StartCoroutine ( EnableInputAfterDuration ( blockInputDuration ) ) ;
}
else
{
Invoke ( nameof ( EnableInput ) , blockInputDuration ) ;
}
}
else
{
if ( InputDeviceManager . autoFocus ) SetFocus ( firstSelected ) ;
if ( s_isInputDisabled ) EnableInput ( ) ;
}
#if TMP_PRESENT
DialogueManager . instance . StartCoroutine ( CheckTMProAutoScroll ( ) ) ;
#endif
}
private IEnumerator EnableInputAfterDuration ( float duration )
{
yield return new WaitForSecondsRealtime ( duration ) ;
EnableInput ( ) ;
}
#if TMP_PRESENT
// Handles edge case where TMPro uses autoscroll but entry ends before typing starts.
// In this case, this method updates the autoscroll size.
protected IEnumerator CheckTMProAutoScroll ( )
{
var ui = GetComponentInParent < StandardDialogueUI > ( ) ;
if ( ui = = null | | ui . conversationUIElements . defaultNPCSubtitlePanel = = null | | ui . conversationUIElements . defaultNPCSubtitlePanel . subtitleText = = null ) yield break ;
var tmp = ui . conversationUIElements . defaultNPCSubtitlePanel . subtitleText . textMeshProUGUI ;
if ( tmp = = null ) yield break ;
var layoutElement = tmp . GetComponent < UnityEngine . UI . LayoutElement > ( ) ;
if ( layoutElement ! = null ) layoutElement . preferredHeight = - 1 ;
var uiScrollbarEnabler = GetComponentInParent < UIScrollbarEnabler > ( ) ;
if ( uiScrollbarEnabler ! = null )
{
yield return null ;
uiScrollbarEnabler . CheckScrollbarWithResetValue ( buttonTemplateScrollbarResetValue ) ;
}
}
#endif
protected virtual IEnumerator ShowAfterPanelsClose ( Subtitle subtitle , Response [ ] responses , Transform target )
{
if ( dialogueUI ! = null )
{
float safeguardTime = Time . realtimeSinceStartup + WaitForCloseTimeoutDuration ;
while ( dialogueUI . AreAnyPanelsClosing ( ) & & Time . realtimeSinceStartup < safeguardTime )
{
yield return null ;
}
}
ShowResponsesNow ( subtitle , responses , target ) ;
}
public virtual void HideResponses ( )
{
StopTimer ( ) ;
Unfocus ( ) ;
Close ( ) ;
}
public override void Close ( )
{
if ( isOpen ) base . Close ( ) ;
}
public virtual void Focus ( )
{
if ( hasFocus ) return ;
if ( panelState = = PanelState . Opening & & enabled & & gameObject . activeInHierarchy )
{
StartCoroutine ( FocusWhenOpen ( ) ) ;
}
else
{
FocusNow ( ) ;
}
}
protected IEnumerator FocusWhenOpen ( )
{
float timeout = Time . realtimeSinceStartup + 5f ;
while ( panelState ! = PanelState . Open & & Time . realtimeSinceStartup < timeout )
{
yield return null ;
}
FocusNow ( ) ;
}
protected virtual void FocusNow ( )
{
panelState = PanelState . Open ;
animatorMonitor . SetTrigger ( focusAnimationTrigger , null , false ) ;
UITools . EnableInteractivity ( gameObject ) ;
if ( hasFocus ) return ;
if ( string . IsNullOrEmpty ( focusAnimationTrigger ) )
{
OnFocused ( ) ;
}
else
{
animatorMonitor . SetTrigger ( focusAnimationTrigger , OnFocused , true ) ;
}
onFocus . Invoke ( ) ;
}
private void OnFocused ( )
{
hasFocus = true ;
}
public virtual void Unfocus ( )
{
if ( ! hasFocus ) return ;
hasFocus = false ;
animatorMonitor . SetTrigger ( unfocusAnimationTrigger , null , false ) ;
onUnfocus . Invoke ( ) ;
}
protected void ActivateUIElements ( )
{
SetUIElementsActive ( true ) ;
}
protected void DeactivateUIElements ( )
{
SetUIElementsActive ( false ) ;
}
protected virtual void SetUIElementsActive ( bool value )
{
Tools . SetGameObjectActive ( panel , value ) ;
Tools . SetGameObjectActive ( pcImage , value & & pcImage ! = null & & pcImage . sprite ! = null ) ;
pcName . SetActive ( value ) ;
Tools . SetGameObjectActive ( timerSlider , false ) ; // Let StartTimer activate if needed.
if ( value = = false ) ClearResponseButtons ( ) ;
}
public virtual void HideImmediate ( )
{
OnHidden ( ) ;
}
protected virtual void ClearResponseButtons ( )
{
DestroyInstantiatedButtons ( ) ;
if ( buttons ! = null )
{
for ( int i = 0 ; i < buttons . Length ; i + + )
{
if ( buttons [ i ] = = null ) continue ;
buttons [ i ] . Reset ( ) ;
buttons [ i ] . isVisible = showUnusedButtons ;
buttons [ i ] . gameObject . SetActive ( showUnusedButtons ) ;
}
}
}
/// <summary>
/// Sets the response buttons.
/// </summary>
/// <param name='responses'>Responses.</param>
/// <param name='target'>Target that will receive OnClick events from the buttons.</param>
protected virtual void SetResponseButtons ( Response [ ] responses , Transform target )
{
firstSelected = null ;
DestroyInstantiatedButtons ( ) ;
var hasDisabledButton = false ;
// Prep autonumber format:
if ( autonumber . enabled )
{
m_processedAutonumberFormat = FormattedText . Parse ( autonumber . format . Replace ( "\\t" , "\t" ) . Replace ( "\\n" , "\n" ) ) . text ;
}
if ( ( buttons ! = null ) & & ( responses ! = null ) )
{
// Add explicitly-positioned buttons:
int buttonNumber = 0 ;
for ( int i = 0 ; i < responses . Length ; i + + )
{
if ( responses [ i ] . formattedText . position ! = FormattedText . NoAssignedPosition )
{
int position = responses [ i ] . formattedText . position ;
if ( 0 < = position & & position < buttons . Length & & buttons [ position ] ! = null )
{
SetResponseButton ( buttons [ position ] , responses [ i ] , target , buttonNumber + + ) ;
}
else
{
Debug . LogWarning ( "Dialogue System: Buttons list doesn't contain a button for position " + position + "." , this ) ;
}
}
}
if ( ( buttonTemplate ! = null ) & & ( buttonTemplateHolder ! = null ) )
{
if ( scrollbarEnabler ! = null ) CheckScrollbar ( ) ;
// Instantiate buttons from template:
for ( int i = 0 ; i < responses . Length ; i + + )
{
if ( responses [ i ] . formattedText . position ! = FormattedText . NoAssignedPosition ) continue ;
GameObject buttonGameObject = InstantiateButton ( ) ;
if ( buttonGameObject = = null )
{
Debug . LogError ( "Dialogue System: Couldn't instantiate response button template." ) ;
}
else
{
instantiatedButtons . Add ( buttonGameObject ) ;
buttonGameObject . transform . SetParent ( buttonTemplateHolder . transform , false ) ;
buttonGameObject . transform . SetAsLastSibling ( ) ;
buttonGameObject . SetActive ( true ) ;
StandardUIResponseButton responseButton = buttonGameObject . GetComponent < StandardUIResponseButton > ( ) ;
SetResponseButton ( responseButton , responses [ i ] , target , buttonNumber + + ) ;
if ( responseButton ! = null )
{
buttonGameObject . name = "Response: " + responseButton . text ;
if ( explicitNavigationForTemplateButtons & & ! responseButton . isClickable ) hasDisabledButton = true ;
}
if ( firstSelected = = null ) firstSelected = buttonGameObject ;
}
}
}
else
{
// Auto-position remaining buttons:
if ( buttonAlignment = = ResponseButtonAlignment . ToFirst )
{
// Align to first, so add in order to front:
for ( int i = 0 ; i < Mathf . Min ( buttons . Length , responses . Length ) ; i + + )
{
if ( responses [ i ] . formattedText . position = = FormattedText . NoAssignedPosition )
{
int position = Mathf . Clamp ( GetNextAvailableResponseButtonPosition ( 0 , 1 ) , 0 , buttons . Length - 1 ) ;
SetResponseButton ( buttons [ position ] , responses [ i ] , target , buttonNumber + + ) ;
if ( firstSelected = = null ) firstSelected = buttons [ position ] . gameObject ;
}
}
}
else
{
// Align to last, so add in reverse order to back:
for ( int i = Mathf . Min ( buttons . Length , responses . Length ) - 1 ; i > = 0 ; i - - )
{
if ( responses [ i ] . formattedText . position = = FormattedText . NoAssignedPosition )
{
int position = Mathf . Clamp ( GetNextAvailableResponseButtonPosition ( buttons . Length - 1 , - 1 ) , 0 , buttons . Length - 1 ) ;
SetResponseButton ( buttons [ position ] , responses [ i ] , target , buttonNumber + + ) ;
firstSelected = buttons [ position ] . gameObject ;
}
}
}
}
}
if ( explicitNavigationForTemplateButtons ) SetupTemplateButtonNavigation ( hasDisabledButton ) ;
NotifyContentChanged ( ) ;
}
protected virtual void CheckScrollbar ( )
{
if ( scrollbarEnabler = = null ) return ;
if ( m_scrollbarCoroutine ! = null ) StopCoroutine ( m_scrollbarCoroutine ) ;
m_scrollbarCoroutine = dialogueUI . StartCoroutine ( CheckScrollbarCoroutine ( ) ) ;
}
protected IEnumerator CheckScrollbarCoroutine ( )
{
var timeout = Time . realtimeSinceStartup + UIAnimatorMonitor . MaxWaitDuration ;
while ( ! isOpen & & Time . realtimeSinceStartup < timeout )
{
yield return null ;
}
if ( buttonTemplateScrollbarResetValue > = 0 )
{
if ( buttonTemplateScrollbar ! = null ) buttonTemplateScrollbar . value = buttonTemplateScrollbarResetValue ;
if ( scrollbarEnabler ! = null )
{
scrollbarEnabler . CheckScrollbarWithResetValue ( buttonTemplateScrollbarResetValue ) ;
}
}
else if ( scrollbarEnabler ! = null )
{
scrollbarEnabler . CheckScrollbar ( ) ;
}
}
protected virtual void SetResponseButton ( StandardUIResponseButton button , Response response , Transform target , int buttonNumber )
{
if ( button ! = null )
{
button . response = response ;
button . gameObject . SetActive ( true ) ;
button . isVisible = true ;
button . isClickable = response . enabled ;
button . target = target ;
if ( response ! = null )
{
if ( warnOnEmptyResponseText & & DialogueDebug . logWarnings & & string . IsNullOrEmpty ( response . formattedText . text ) )
{
Debug . LogWarning ( $"Dialogue System: Response entry [{response.destinationEntry.id}] menu text is blank." , button ) ;
}
button . SetFormattedText ( response . formattedText ) ;
}
// Auto-number:
if ( autonumber . enabled )
{
button . text = string . Format ( m_processedAutonumberFormat , buttonNumber + 1 , button . text ) ;
// Add UIButtonKeyTrigger(s) if needed:
var numKeyTriggersNeeded = 0 ;
if ( autonumber . regularNumberHotkeys ) numKeyTriggersNeeded + + ;
if ( autonumber . numpadHotkeys ) numKeyTriggersNeeded + + ;
var keyTriggers = button . GetComponents < UIButtonKeyTrigger > ( ) ;
if ( keyTriggers . Length < numKeyTriggersNeeded )
{
for ( int i = keyTriggers . Length ; i < numKeyTriggersNeeded ; i + + )
{
button . gameObject . AddComponent < UIButtonKeyTrigger > ( ) ;
}
keyTriggers = button . GetComponents < UIButtonKeyTrigger > ( ) ;
}
int index = 0 ;
if ( autonumber . regularNumberHotkeys )
{
keyTriggers [ index + + ] . key = ( KeyCode ) ( ( int ) KeyCode . Alpha1 + buttonNumber ) ;
}
if ( autonumber . numpadHotkeys )
{
keyTriggers [ index ] . key = ( KeyCode ) ( ( int ) KeyCode . Keypad1 + buttonNumber ) ;
}
}
}
}
protected int GetNextAvailableResponseButtonPosition ( int start , int direction )
{
if ( buttons ! = null )
{
int position = start ;
while ( ( 0 < = position ) & & ( position < buttons . Length ) )
{
if ( buttons [ position ] . isVisible & & buttons [ position ] . response ! = null )
{
position + = direction ;
}
else
{
return position ;
}
}
}
return 5 ;
}
public virtual void SetupTemplateButtonNavigation ( bool hasDisabledButton )
{
// Assumes buttons are active (since uses GetComponent), so call after activating panel.
if ( instantiatedButtons = = null | | instantiatedButtons . Count = = 0 ) return ;
var buttons = new List < GameObject > ( ) ;
if ( hasDisabledButton )
{
// If some buttons are disabled, make a list of only the clickable ones:
buttons . AddRange ( instantiatedButtons . FindAll ( x = > x . GetComponent < StandardUIResponseButton > ( ) . isClickable ) ) ;
}
else
{
buttons . AddRange ( instantiatedButtons ) ;
}
for ( int i = 0 ; i < buttons . Count ; i + + )
{
var button = buttons [ i ] . GetComponent < UnityEngine . UI . Button > ( ) ;
if ( button = = null ) continue ;
var above = ( i = = 0 ) ? ( loopExplicitNavigation ? buttons [ buttons . Count - 1 ] . GetComponent < UnityEngine . UI . Button > ( ) : null )
: buttons [ i - 1 ] . GetComponent < UnityEngine . UI . Button > ( ) ;
var below = ( i = = buttons . Count - 1 ) ? ( loopExplicitNavigation ? buttons [ 0 ] . GetComponent < UnityEngine . UI . Button > ( ) : null )
: buttons [ i + 1 ] . GetComponent < UnityEngine . UI . Button > ( ) ;
var navigation = new UnityEngine . UI . Navigation ( ) ;
navigation . mode = UnityEngine . UI . Navigation . Mode . Explicit ;
navigation . selectOnUp = above ;
navigation . selectOnLeft = above ;
navigation . selectOnDown = below ;
navigation . selectOnRight = below ;
button . navigation = navigation ;
}
}
protected virtual GameObject InstantiateButton ( )
{
// Try to pull from pool first:
if ( m_instantiatedButtonPool . Count > 0 )
{
var button = m_instantiatedButtonPool [ 0 ] ;
m_instantiatedButtonPool . RemoveAt ( 0 ) ;
return button ;
}
else
{
return GameObject . Instantiate ( buttonTemplate . gameObject ) as GameObject ;
}
}
public void DestroyInstantiatedButtons ( )
{
// Return buttons to pool:
for ( int i = 0 ; i < instantiatedButtons . Count ; i + + )
{
instantiatedButtons [ i ] . SetActive ( false ) ;
}
m_instantiatedButtonPool . AddRange ( instantiatedButtons ) ;
instantiatedButtons . Clear ( ) ;
NotifyContentChanged ( ) ;
}
/// <summary>
/// Makes the panel's buttons non-clickable.
/// Typically called by the dialogue UI as soon as a button has been
/// clicked to make sure the player can't click another one while the
/// menu is playing its hide animation.
/// </summary>
public virtual void MakeButtonsNonclickable ( )
{
for ( int i = 0 ; i < instantiatedButtons . Count ; i + + )
{
var responseButton = ( instantiatedButtons [ i ] ! = null ) ? instantiatedButtons [ i ] . GetComponent < StandardUIResponseButton > ( ) : null ;
if ( responseButton ! = null ) responseButton . isClickable = false ;
}
for ( int i = 0 ; i < buttons . Length ; i + + )
{
if ( buttons [ i ] ! = null ) buttons [ i ] . isClickable = false ;
}
}
protected void NotifyContentChanged ( )
{
onContentChanged . Invoke ( ) ;
}
protected void DisableInput ( )
{
SetInput ( false ) ;
}
protected void EnableInput ( )
{
SetInput ( true ) ;
}
protected void SetInput ( bool value )
{
s_isInputDisabled = ( value = = false ) ;
if ( m_mainCanvasGroup = = null )
{
// Try to get dialogue UI's main panel:
var ui = GetComponentInParent < StandardDialogueUI > ( ) ;
if ( ui ! = null & & ui . conversationUIElements . mainPanel ! = null )
{
var mainPanel = ui . conversationUIElements . mainPanel ;
m_mainCanvasGroup = mainPanel . GetComponent < CanvasGroup > ( ) ? ? mainPanel . gameObject . AddComponent < CanvasGroup > ( ) ;
}
else
{
// Otherwise try the menu's panel:
var menuPanel = panel ;
if ( menuPanel = = null ) menuPanel = buttonTemplateHolder ;
if ( menuPanel ! = null )
{
m_mainCanvasGroup = menuPanel . GetComponent < CanvasGroup > ( ) ? ? menuPanel . gameObject . AddComponent < CanvasGroup > ( ) ;
}
}
}
if ( m_mainCanvasGroup ! = null ) m_mainCanvasGroup . interactable = value ;
if ( value = = false )
{
// If auto focus, show firstSelected in selected state:
if ( InputDeviceManager . autoFocus & & firstSelected ! = null )
{
var button = firstSelected . GetComponent < UnityEngine . UI . Button > ( ) ;
MethodInfo methodInfo = typeof ( UnityEngine . UI . Button ) . GetMethod ( "DoStateTransition" , BindingFlags . Instance | BindingFlags . NonPublic ) ;
methodInfo . Invoke ( button , new object [ ] { 3 , true } ) ; // 3 = SelectionState.Selected
}
}
if ( eventSystem ! = null )
{
var inputModule = eventSystem . GetComponent < UnityEngine . EventSystems . PointerInputModule > ( ) ;
if ( inputModule ! = null ) inputModule . enabled = value ;
}
UIButtonKeyTrigger . monitorInput = value ;
if ( value = = true )
{
RefreshSelectablesList ( ) ;
CheckFocus ( ) ;
if ( eventSystem ! = null & & eventSystem . currentSelectedGameObject ! = null )
{ // Also show in focused/selected state:
UIUtility . Select ( eventSystem . currentSelectedGameObject . GetComponent < UnityEngine . UI . Selectable > ( ) ) ;
}
}
}
#endregion
#region Timer
/// <summary>
/// Starts the timer.
/// </summary>
/// <param name='timeout'>Timeout duration in seconds.</param>
/// <param name="timeoutHandler">Invoke this handler on timeout.</param>
public virtual void StartTimer ( float timeout , System . Action timeoutHandler )
{
if ( m_timer = = null )
{
if ( timerSlider ! = null )
{
Tools . SetGameObjectActive ( timerSlider , true ) ;
m_timer = timerSlider . GetComponent < StandardUITimer > ( ) ;
if ( m_timer = = null ) m_timer = timerSlider . gameObject . AddComponent < StandardUITimer > ( ) ;
}
else
{
m_timer = GetComponentInChildren < StandardUITimer > ( ) ;
if ( m_timer = = null ) m_timer = gameObject . AddComponent < StandardUITimer > ( ) ;
}
}
Tools . SetGameObjectActive ( m_timer , true ) ;
m_timer . StartCountdown ( timeout , timeoutHandler ) ;
}
public virtual void StopTimer ( )
{
if ( m_timer ! = null )
{
m_timer . StopCountdown ( ) ;
Tools . SetGameObjectActive ( m_timer , false ) ;
}
}
#endregion
}
}