309 lines
13 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|