using UnityEngine;
using UnityEngine.UI;
///
/// Top banner with three buttons (1=Live View, 2=Inverse Kinematics, 3=Programmed Movements).
/// Each button enables the corresponding view GameObject and disables the other two.
/// View-specific logic should be attached under those GameObjects; when disabled, it is suspended.
///
public class ViewBannerUI : MonoBehaviour
{
private const string ViewsParentName = "Views";
private const string LiveViewName = "LiveView";
private const string InverseKinematicsViewName = "InverseKinematicsView";
private const string ProgrammedMovementsViewName = "ProgrammedMovementsView";
[Header("Layout")]
[SerializeField] private float bannerHeight = 48f;
[SerializeField] private float buttonPadding = 6f;
[Header("Colors")]
[SerializeField] private Color bannerBackground = Color.black;
[SerializeField] private Color buttonDefault = Color.white;
[SerializeField] private Color buttonSelected = new Color(177f / 255f, 1f, 199f / 255f, 1f); // #B1FFC7
[SerializeField] private Color buttonTextColor = Color.black;
private GameObject _liveViewRoot;
private GameObject _inverseKinematicsViewRoot;
private GameObject _programmedMovementsViewRoot;
private RectTransform _bannerRoot;
private Image[] _buttonImages = new Image[3];
// Labels without "View": Live, Inverse Kinematics, Programmed Movements
private static readonly string[] Labels = { "Live", "Inverse Kinematics", "Programmed Movements" };
private void Awake()
{
if (!ResolveViewRoots())
return;
// Use this GameObject as the banner (must be under Canvas with RectTransform + Image).
_bannerRoot = GetComponent();
if (_bannerRoot == null)
{
Debug.LogError("[ViewBannerUI] ViewBanner must have a RectTransform (e.g. under Canvas).");
return;
}
var bannerImage = GetComponent();
if (bannerImage != null)
bannerImage.color = new Color(0f, 0f, 0f, 1f); // solid black, full opacity
// Default to Live View on startup.
SetActiveView(ViewMode.LiveView);
for (int i = 0; i < 3; i++)
{
ViewMode mode = (ViewMode)i;
_buttonImages[i] = CreateViewButton(_bannerRoot, i, Labels[i], () => OnViewButtonClicked(mode));
}
RefreshButtonVisuals();
}
///
/// Resolves the three view root GameObjects that must already exist in the scene hierarchy:
/// Views/LiveView, Views/InverseKinematicsView, Views/ProgrammedMovementsView.
/// Returns false and logs a clear error if anything is missing.
///
private bool ResolveViewRoots()
{
Transform viewsParent = GameObject.Find(ViewsParentName)?.transform;
if (viewsParent == null)
{
Debug.LogError(
$"[ViewBannerUI] Missing '{ViewsParentName}' GameObject. " +
$"Please create it in the scene with children '{LiveViewName}', '{InverseKinematicsViewName}', '{ProgrammedMovementsViewName}'."
);
return false;
}
_liveViewRoot = viewsParent.Find(LiveViewName)?.gameObject;
_inverseKinematicsViewRoot = viewsParent.Find(InverseKinematicsViewName)?.gameObject;
_programmedMovementsViewRoot = viewsParent.Find(ProgrammedMovementsViewName)?.gameObject;
if (_liveViewRoot == null || _inverseKinematicsViewRoot == null || _programmedMovementsViewRoot == null)
{
Debug.LogError(
$"[ViewBannerUI] Missing one or more view roots under '{ViewsParentName}'. " +
$"Expected children named '{LiveViewName}', '{InverseKinematicsViewName}', '{ProgrammedMovementsViewName}'."
);
return false;
}
return true;
}
private void OnViewButtonClicked(ViewMode mode)
{
SetActiveView(mode);
RefreshButtonVisuals();
}
///
/// Enables the view GameObject for the given mode and disables the other two.
///
public void SetActiveView(ViewMode mode)
{
_liveViewRoot.SetActive(mode == ViewMode.LiveView);
_inverseKinematicsViewRoot.SetActive(mode == ViewMode.InverseKinematics);
_programmedMovementsViewRoot.SetActive(mode == ViewMode.ProgrammedMovements);
}
private void RefreshButtonVisuals()
{
bool liveActive = _liveViewRoot != null && _liveViewRoot.activeSelf;
bool ikActive = _inverseKinematicsViewRoot != null && _inverseKinematicsViewRoot.activeSelf;
bool pmActive = _programmedMovementsViewRoot != null && _programmedMovementsViewRoot.activeSelf;
if (_buttonImages[0] != null) _buttonImages[0].color = liveActive ? buttonSelected : buttonDefault;
if (_buttonImages[1] != null) _buttonImages[1].color = ikActive ? buttonSelected : buttonDefault;
if (_buttonImages[2] != null) _buttonImages[2].color = pmActive ? buttonSelected : buttonDefault;
}
private Image CreateViewButton(RectTransform parent, int index, string label, UnityEngine.Events.UnityAction onClick)
{
var go = new GameObject($"ViewButton_{label}");
go.transform.SetParent(parent, false);
var rect = go.AddComponent();
// Buttons at 1/6 width each, centered as a group (1/4 margin each side)
float sixth = 1f / 6f;
float startX = 0.25f; // left margin so block is centered
rect.anchorMin = new Vector2(startX + index * sixth, 0f);
rect.anchorMax = new Vector2(startX + (index + 1) * sixth, 1f);
rect.pivot = new Vector2(0.5f, 0.5f);
rect.anchoredPosition = Vector2.zero;
rect.sizeDelta = Vector2.zero;
rect.offsetMin = new Vector2(buttonPadding, buttonPadding);
rect.offsetMax = new Vector2(-buttonPadding, -buttonPadding);
var image = go.AddComponent();
image.color = buttonDefault;
image.raycastTarget = true;
image.sprite = UIRoundedSprite.GetRoundedRectSprite();
image.type = Image.Type.Simple;
var button = go.AddComponent