// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI { /// /// HoloLens 2 shell's style button specific elements /// [AddComponentMenu("Scripts/MRTK/SDK/PressableButtonHoloLens2")] public class PressableButtonHoloLens2 : PressableButton { [SerializeField] [Tooltip("The icon and text content moving inside the button.")] private GameObject movingButtonIconText = null; [SerializeField] [Tooltip("The visuals which become compressed (scaled) along the z-axis when pressed.")] private GameObject compressableButtonVisuals = null; /// /// The visuals which become compressed (scaled) along the z-axis when pressed. /// public GameObject CompressableButtonVisuals { get => compressableButtonVisuals; set { compressableButtonVisuals = value; if (compressableButtonVisuals != null) { initialCompressableButtonVisualsLocalScale = compressableButtonVisuals.transform.localScale; } } } [SerializeField] [Range(0.0f, 1.0f)] [Tooltip("The minimum percentage of the original scale the compressableButtonVisuals can be compressed to.")] private float minCompressPercentage = 0.25f; /// /// The minimum percentage of the original scale the compressableButtonVisuals can be compressed to. /// public float MinCompressPercentage { get => minCompressPercentage; set => minCompressPercentage = value; } [SerializeField] [Tooltip("The plate which represents the press-able surface of the button that highlights when focused.")] private Renderer highlightPlate = null; [SerializeField] [Tooltip("The duration of time it takes to animate in/out the highlight plate.")] private float highlightPlateAnimationTime = 0.25f; #region Private Members Vector3 initialCompressableButtonVisualsLocalScale = Vector3.one; private int fluentLightIntensityID = 0; private float targetFluentLightIntensity = 1.0f; private MaterialPropertyBlock properties = null; private Coroutine highlightPlateAnimationRoutine = null; #endregion protected override void Start() { base.Start(); if (compressableButtonVisuals != null) { initialCompressableButtonVisualsLocalScale = compressableButtonVisuals.transform.localScale; } if (highlightPlate != null) { // Cache the initial highlight plate state. fluentLightIntensityID = Shader.PropertyToID("_FluentLightIntensity"); properties = new MaterialPropertyBlock(); targetFluentLightIntensity = highlightPlate.sharedMaterial.GetFloat(fluentLightIntensityID); // Hide the highlight plate initially. UpdateHightlightPlateVisuals(0.0f); highlightPlate.enabled = false; } } /// /// Public property to set the moving content part(icon and text) of the button. /// This content part moves 1/2 distance of the front cage /// public GameObject MovingButtonIconText { get { return movingButtonIconText; } set { if (movingButtonIconText != value) { movingButtonIconText = value; } } } /// protected override void UpdateMovingVisualsPosition() { base.UpdateMovingVisualsPosition(); if (compressableButtonVisuals != null) { // Compress the button visuals by the push amount. Vector3 scale = compressableButtonVisuals.transform.localScale; float pressPercentage; // Prevent divide by zero when calculating pressPercentage. if (MaxPushDistance <= float.Epsilon) { pressPercentage = 0.0f; } else { pressPercentage = Mathf.Max(minCompressPercentage, (1.0f - (CurrentPushDistance - startPushDistance) / MaxPushDistance)); } scale.z = initialCompressableButtonVisualsLocalScale.z * pressPercentage; compressableButtonVisuals.transform.localScale = scale; } if (movingButtonIconText != null) { // Always move relative to startPushDistance movingButtonIconText.transform.localPosition = GetLocalPositionAlongPushDirection((CurrentPushDistance - startPushDistance) / 2.0f); } } /// /// Animates in the highlight plate. /// public void AnimateInHighlightPlate() { if (highlightPlate != null) { if (highlightPlateAnimationRoutine != null) { StopCoroutine(highlightPlateAnimationRoutine); } highlightPlateAnimationRoutine = StartCoroutine(AnimateHighlightPlate(true, highlightPlateAnimationTime)); } } /// /// Animates out the highlight plate and disables it when animated out. /// public void AnimateOutHighlightPlate() { if (highlightPlate != null) { if (highlightPlateAnimationRoutine != null) { StopCoroutine(highlightPlateAnimationRoutine); } highlightPlateAnimationRoutine = StartCoroutine(AnimateHighlightPlate(false, highlightPlateAnimationTime)); } } private IEnumerator AnimateHighlightPlate(bool fadeIn, float time) { highlightPlate.enabled = true; // Calculate how much time is left in the blend based on current intensity. var normalizedIntensity = (targetFluentLightIntensity != 0.0f) ? properties.GetFloat(fluentLightIntensityID) / targetFluentLightIntensity : 1.0f; var blendTime = fadeIn ? (1.0f - normalizedIntensity) * time : normalizedIntensity * time; while (blendTime > 0.0f) { float t = 1.0f - (blendTime / time); UpdateHightlightPlateVisuals(fadeIn ? t : 1.0f - t); blendTime -= Time.deltaTime; yield return null; } UpdateHightlightPlateVisuals(fadeIn ? targetFluentLightIntensity : 0.0f); // When completely faded out, hide the highlight plate. if (!fadeIn) { highlightPlate.enabled = false; } } private void UpdateHightlightPlateVisuals(float lightIntensity) { highlightPlate.GetPropertyBlock(properties); properties.SetFloat(fluentLightIntensityID, lightIntensity); highlightPlate.SetPropertyBlock(properties); } } }