ls_unity/Scripts/UI/Window.cs

309 lines
13 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
namespace UI
{
/// <summary>
/// A draggable window component with title bar dragging and close button functionality.
/// Automatically creates UI structure on Awake.
/// </summary>
public class Window : MonoBehaviour
{
[Header("Window Settings")]
[SerializeField] private Vector2 windowSize = new Vector2(600, 400);
[SerializeField] private string windowTitle = "Window";
[SerializeField] private Color backgroundColor = new Color(0.2f, 0.2f, 0.2f, 1f);
[SerializeField] private Color titleBarColor = new Color(0.3f, 0.3f, 0.3f, 1f);
[SerializeField] private float titleBarHeight = 30f;
private RectTransform rectTransform;
private RectTransform titleBarRect;
private Image titleBarImage;
private bool isDragging = false;
private Vector2 dragOffset;
private bool wasMouseDownLastFrame = false;
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
if (rectTransform == null)
{
rectTransform = gameObject.AddComponent<RectTransform>();
}
CreateUIStructure();
}
private void Update()
{
// Fallback: Check for mouse down on title bar if interface didn't fire
bool isMouseDown = Mouse.current.leftButton.isPressed;
if (isMouseDown && !wasMouseDownLastFrame && !isDragging)
{
CheckTitleBarClick();
}
wasMouseDownLastFrame = isMouseDown;
HandleDragging();
}
/// <summary>
/// Fallback method to check if mouse clicked on title bar using raycasting
/// </summary>
private void CheckTitleBarClick()
{
Debug.Log("CheckTitleBarClick");
if (EventSystem.current == null) return;
Vector2 mousePosition = Mouse.current.position.ReadValue();
// Use EventSystem to raycast
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = mousePosition;
var results = new System.Collections.Generic.List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
foreach (var result in results)
{
// Check if we hit the title bar (but not the close button or its children)
Transform hitTransform = result.gameObject.transform;
if (hitTransform.name == "TitleBar" && hitTransform.IsChildOf(transform))
{
OnTitleBarPointerDown(mousePosition);
break;
}
// If we hit a child of TitleBar, check if it's not the close button
else if (hitTransform.IsChildOf(transform.Find("TitleBar")))
{
// Skip if it's the close button or its children
if (!hitTransform.name.Contains("CloseButton") && hitTransform.parent != null && hitTransform.parent.name != "CloseButton")
{
OnTitleBarPointerDown(mousePosition);
break;
}
}
}
}
/// <summary>
/// Creates the UI structure: background, title bar, close button, and content area
/// </summary>
private void CreateUIStructure()
{
// Set up root RectTransform
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.sizeDelta = windowSize;
rectTransform.anchoredPosition = Vector2.zero;
// Create Background
GameObject backgroundObj = new GameObject("Background");
backgroundObj.transform.SetParent(transform, false);
Image backgroundImage = backgroundObj.AddComponent<Image>();
backgroundImage.color = backgroundColor;
RectTransform backgroundRect = backgroundObj.GetComponent<RectTransform>();
backgroundRect.anchorMin = Vector2.zero;
backgroundRect.anchorMax = Vector2.one;
backgroundRect.sizeDelta = Vector2.zero;
backgroundRect.anchoredPosition = Vector2.zero;
// Create Title Bar
GameObject titleBarObj = new GameObject("TitleBar");
titleBarObj.transform.SetParent(transform, false);
Image titleBarImage = titleBarObj.AddComponent<Image>();
titleBarImage.color = titleBarColor;
titleBarRect = titleBarObj.GetComponent<RectTransform>();
titleBarRect.anchorMin = new Vector2(0, 1);
titleBarRect.anchorMax = new Vector2(1, 1);
titleBarRect.pivot = new Vector2(0.5f, 1);
titleBarRect.sizeDelta = new Vector2(0, titleBarHeight);
titleBarRect.anchoredPosition = new Vector2(0, 0);
this.titleBarImage = titleBarImage;
// Add TitleBarDragHandler component for dragging
TitleBarDragHandler dragHandler = titleBarObj.AddComponent<TitleBarDragHandler>();
dragHandler.Initialize(this);
// Create Title Text
GameObject titleTextObj = new GameObject("TitleText");
titleTextObj.transform.SetParent(titleBarObj.transform, false);
Text titleText = titleTextObj.AddComponent<Text>();
titleText.text = windowTitle;
titleText.color = Color.white;
titleText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
titleText.fontSize = 14;
titleText.alignment = TextAnchor.MiddleLeft;
RectTransform titleTextRect = titleTextObj.GetComponent<RectTransform>();
titleTextRect.anchorMin = new Vector2(0, 0);
titleTextRect.anchorMax = new Vector2(1, 1);
titleTextRect.sizeDelta = new Vector2(-60, 0); // Leave space for close button
titleTextRect.anchoredPosition = new Vector2(10, 0);
// Create Close Button
GameObject closeButtonObj = new GameObject("CloseButton");
closeButtonObj.transform.SetParent(titleBarObj.transform, false);
Image closeButtonImage = closeButtonObj.AddComponent<Image>();
closeButtonImage.color = new Color(0.5f, 0.5f, 0.5f, 1f);
Button closeButton = closeButtonObj.AddComponent<Button>();
ColorBlock colors = closeButton.colors;
colors.normalColor = new Color(0.5f, 0.5f, 0.5f, 1f);
colors.highlightedColor = new Color(0.8f, 0.2f, 0.2f, 1f);
colors.pressedColor = new Color(0.6f, 0.1f, 0.1f, 1f);
closeButton.colors = colors;
closeButton.onClick.AddListener(OnCloseButtonClicked);
RectTransform closeButtonRect = closeButtonObj.GetComponent<RectTransform>();
closeButtonRect.anchorMin = new Vector2(1, 0);
closeButtonRect.anchorMax = new Vector2(1, 1);
closeButtonRect.pivot = new Vector2(1, 0.5f);
closeButtonRect.sizeDelta = new Vector2(30, 0);
closeButtonRect.anchoredPosition = new Vector2(0, 0);
// Create Close Button Text (X)
GameObject closeButtonTextObj = new GameObject("Text");
closeButtonTextObj.transform.SetParent(closeButtonObj.transform, false);
Text closeButtonText = closeButtonTextObj.AddComponent<Text>();
closeButtonText.text = "X";
closeButtonText.color = Color.white;
closeButtonText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
closeButtonText.fontSize = 16;
closeButtonText.alignment = TextAnchor.MiddleCenter;
closeButtonText.fontStyle = FontStyle.Bold;
RectTransform closeButtonTextRect = closeButtonTextObj.GetComponent<RectTransform>();
closeButtonTextRect.anchorMin = Vector2.zero;
closeButtonTextRect.anchorMax = Vector2.one;
closeButtonTextRect.sizeDelta = Vector2.zero;
closeButtonTextRect.anchoredPosition = Vector2.zero;
// Create Content Area
GameObject contentAreaObj = new GameObject("ContentArea");
contentAreaObj.transform.SetParent(transform, false);
RectTransform contentAreaRect = contentAreaObj.AddComponent<RectTransform>();
contentAreaRect.anchorMin = new Vector2(0, 0);
contentAreaRect.anchorMax = new Vector2(1, 1);
contentAreaRect.sizeDelta = new Vector2(0, -titleBarHeight);
contentAreaRect.anchoredPosition = new Vector2(0, 0);
// Create Placeholder Text
GameObject placeholderTextObj = new GameObject("PlaceholderText");
placeholderTextObj.transform.SetParent(contentAreaObj.transform, false);
Text placeholderText = placeholderTextObj.AddComponent<Text>();
placeholderText.text = "Content Area";
placeholderText.color = new Color(0.8f, 0.8f, 0.8f, 1f);
placeholderText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
placeholderText.fontSize = 12;
placeholderText.alignment = TextAnchor.MiddleCenter;
RectTransform placeholderTextRect = placeholderTextObj.GetComponent<RectTransform>();
placeholderTextRect.anchorMin = Vector2.zero;
placeholderTextRect.anchorMax = Vector2.one;
placeholderTextRect.sizeDelta = Vector2.zero;
placeholderTextRect.anchoredPosition = Vector2.zero;
}
/// <summary>
/// Called when pointer is pressed down on the title bar
/// </summary>
public void OnTitleBarPointerDown(Vector2 screenPosition)
{
isDragging = true;
RectTransform parentRect = rectTransform.parent as RectTransform;
if (parentRect != null)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRect,
screenPosition,
null,
out Vector2 localPoint
);
// Calculate offset from mouse position to window's current position
dragOffset = localPoint - rectTransform.anchoredPosition;
}
}
/// <summary>
/// Handles window dragging in Update loop
/// </summary>
private void HandleDragging()
{
if (isDragging)
{
if (Mouse.current.leftButton.isPressed)
{
Vector2 mousePosition = Mouse.current.position.ReadValue();
RectTransform parentRect = rectTransform.parent as RectTransform;
if (parentRect != null)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRect,
mousePosition,
null,
out Vector2 localPoint
);
rectTransform.anchoredPosition = localPoint - dragOffset;
}
}
else
{
isDragging = false;
}
}
}
/// <summary>
/// Called when close button is clicked
/// </summary>
private void OnCloseButtonClicked()
{
Destroy(gameObject);
}
/// <summary>
/// Sets the window title
/// </summary>
public void SetTitle(string title)
{
windowTitle = title;
Transform titleTextTransform = transform.Find("TitleBar/TitleText");
if (titleTextTransform != null)
{
Text titleText = titleTextTransform.GetComponent<Text>();
if (titleText != null)
{
titleText.text = title;
}
}
}
}
/// <summary>
/// Helper component for handling title bar drag events
/// </summary>
public class TitleBarDragHandler : MonoBehaviour, IPointerDownHandler
{
private Window window;
public void Initialize(Window windowComponent)
{
window = windowComponent;
}
public void OnPointerDown(PointerEventData eventData)
{
if (window != null)
{
window.OnTitleBarPointerDown(eventData.position);
}
}
}
}