mouse event 처리 중
This commit is contained in:
@@ -4,6 +4,7 @@ using UnityEngine.UI;
|
||||
using UVC.Locale;
|
||||
using UVC.Log;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.UI.Modal;
|
||||
|
||||
namespace UVC.UI.Menu
|
||||
{
|
||||
@@ -226,7 +227,22 @@ namespace UVC.UI.Menu
|
||||
}),
|
||||
new MenuItemData("preferences", "menu_preferences", new DebugLogCommand("환경설정 선택됨 (Command 실행)"))
|
||||
}));
|
||||
|
||||
model.MenuItems.Add(new MenuItemData("modal", "모달", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
new MenuItemData("alert", "Alert", new ActionCommand(async () => {
|
||||
await Alert.Show("알림", "이것은 간단한 알림 메시지입니다.");
|
||||
await Alert.Show("경고", "데이터를 저장할 수 없습니다.", "알겠습니다");
|
||||
await Alert.Show("error", "error_network_not", "button_retry");
|
||||
})),
|
||||
new MenuItemData("confirm", "Confirm", new ActionCommand(async () => {
|
||||
bool result = await Confirm.Show("확인", "이것은 간단한 알림 메시지입니다.");
|
||||
ULog.Debug($"사용자가 확인 버튼을 눌렀나요? {result}");
|
||||
result = await Confirm.Show("경고", "데이터를 저장할 수 없습니다.", "알겠습니다", "아니요");
|
||||
ULog.Debug($"사용자가 알림을 확인했나요? {result}");
|
||||
result = await Confirm.Show("error", "error_network_not", "button_retry", "button_cancel");
|
||||
ULog.Debug($"사용자가 네트워크 오류 알림을 확인했나요? {result}");
|
||||
}))
|
||||
}));
|
||||
model.MenuItems.Add(new MenuItemData("language", "menu_language", subMenuItems: new List<MenuItemData>
|
||||
{
|
||||
// 각 언어 메뉴 아이템에 ChangeLanguageCommand를 연결하여 언어 변경 기능 수행
|
||||
|
||||
@@ -166,8 +166,7 @@ namespace UVC.UI.Menu
|
||||
{
|
||||
uiBlockerInstance = new GameObject("TopMenuUIBlocker");
|
||||
// Canvas를 찾아 그 자식으로 설정합니다. 씬에 여러 Canvas가 있다면, 적절한 Canvas를 찾는 로직이 필요할 수 있습니다.
|
||||
// 여기서는 FindFirstObjectByType을 사용하여 첫 번째 활성 Canvas를 찾습니다.
|
||||
Canvas canvas = FindFirstObjectByType<Canvas>();
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
Transform blockerParent = canvas != null ? canvas.transform : transform.parent; // Canvas가 없으면 TopMenuView의 부모를 사용
|
||||
|
||||
if (blockerParent == null) // 부모를 찾지 못한 극단적인 경우, TopMenuView 자신을 부모로 설정 (권장되지 않음)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System; // System.Type 사용을 위해 추가
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
using UVC.Log;
|
||||
using UVC.util;
|
||||
|
||||
namespace UVC.UI.Modal
|
||||
{
|
||||
@@ -133,7 +134,7 @@ namespace UVC.UI.Modal
|
||||
if (blockerPrefabObj != null)
|
||||
{
|
||||
// 화면에서 가장 큰 그림판(Canvas)을 찾아서 그 위에 방패를 놓을 거예요.
|
||||
Canvas mainCanvasForBlocker = UnityEngine.Object.FindFirstObjectByType<Canvas>();
|
||||
Canvas mainCanvasForBlocker = CanvasUtil.GetOrCreate("ModalCanvas");
|
||||
if (mainCanvasForBlocker != null)
|
||||
{
|
||||
// 방패를 복제해서(Instantiate) 그림판 위에 놓고, 가장 위로 오도록 순서를 조정해요.
|
||||
@@ -162,7 +163,7 @@ namespace UVC.UI.Modal
|
||||
}
|
||||
|
||||
// 모달 창도 가장 큰 그림판 위에 놓을 거예요.
|
||||
Canvas mainCanvasForModal = UnityEngine.Object.FindFirstObjectByType<Canvas>();
|
||||
Canvas mainCanvasForModal = CanvasUtil.GetOrCreate("ModalCanvas");
|
||||
if (mainCanvasForModal == null) // 그림판을 못 찾으면,
|
||||
{
|
||||
ULog.Error("[Modal] 모달을 표시할 Canvas를 찾을 수 없습니다.");
|
||||
@@ -198,7 +199,7 @@ namespace UVC.UI.Modal
|
||||
{
|
||||
ULog.Debug("[Modal] 활성 모달 인스턴스가 외부에서 파괴되어 취소로 처리합니다.");
|
||||
// 파괴된 모달에서 ModalView를 가져오려고 시도해요 (없을 수도 있지만).
|
||||
ModalView viewOnDestroy = currentModalInstance != null ? currentModalInstance.GetComponent<ModalView>() : null;
|
||||
ModalView? viewOnDestroy = currentModalInstance != null ? currentModalInstance.GetComponent<ModalView>() : null;
|
||||
// 그리고 "외부에서 파괴됐으니 취소할게요" 라고 알리면서 정리해요.
|
||||
await CleanupCurrentModalResources(currentContent, viewOnDestroy, false, true, tcs, typeof(T));
|
||||
}
|
||||
|
||||
@@ -302,16 +302,16 @@ namespace UVC.UI.Toolbar.View
|
||||
public void Update()
|
||||
{
|
||||
// 마우스 왼쪽 버튼이 클릭되었고, 하위 메뉴가 열려있는 상태일 때
|
||||
if (Input.GetMouseButtonDown(0) && _currentSubMenu != null && _currentSubMenu.activeSelf && _view.rootCanvas != null)
|
||||
if (Input.GetMouseButtonDown(0) && _currentSubMenu != null && _currentSubMenu.activeSelf && _view.Canvas != null)
|
||||
{
|
||||
RectTransform subMenuRect = _currentSubMenu.GetComponent<RectTransform>();
|
||||
if (subMenuRect == null) return;
|
||||
|
||||
// 캔버스의 렌더 모드에 따라 이벤트 카메라를 가져옵니다.
|
||||
Camera eventCamera = null;
|
||||
if (_view.rootCanvas.renderMode == RenderMode.ScreenSpaceCamera || _view.rootCanvas.renderMode == RenderMode.WorldSpace)
|
||||
if (_view.Canvas.renderMode == RenderMode.ScreenSpaceCamera || _view.Canvas.renderMode == RenderMode.WorldSpace)
|
||||
{
|
||||
eventCamera = _view.rootCanvas.worldCamera;
|
||||
eventCamera = _view.Canvas.worldCamera;
|
||||
}
|
||||
|
||||
// 마우스 포인터가 하위 메뉴 영역 바깥에 있는지 확인합니다.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -6,6 +7,7 @@ using UVC.Extension;
|
||||
using UVC.Locale;
|
||||
using UVC.UI.Toolbar.Model;
|
||||
using UVC.UI.Tooltip;
|
||||
using UVC.util;
|
||||
|
||||
namespace UVC.UI.Toolbar.View
|
||||
{
|
||||
@@ -112,16 +114,24 @@ namespace UVC.UI.Toolbar.View
|
||||
/// </summary>
|
||||
private Dictionary<System.Type, IButtonViewProcessor> _buttonViewProcessors = new Dictionary<System.Type, IButtonViewProcessor>();
|
||||
|
||||
/// <summary>
|
||||
/// 툴팁 표시에 사용될 루트 Canvas입니다.
|
||||
/// Inspector에서 할당하거나, Awake에서 자동으로 찾으려고 시도합니다.
|
||||
/// </summary>
|
||||
internal Canvas rootCanvas;
|
||||
|
||||
// --- 헬퍼 클래스 ---
|
||||
private ToggleGroupManager _toggleGroupManager;
|
||||
private SubMenuHandler _subMenuHandler;
|
||||
|
||||
private Canvas _canvas;
|
||||
/// <summary>
|
||||
/// 렌더링 작업에 사용되는 정적 캔버스 인스턴스를 가져옵니다.
|
||||
/// </summary>
|
||||
public Canvas Canvas
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_canvas == null) _canvas = CanvasUtil.GetOrCreate("StaticCanvas");
|
||||
return _canvas;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MonoBehaviour의 Awake 메서드입니다.
|
||||
/// 필수 참조를 확인 및 초기화하고, 버튼 뷰 프로세서와 헬퍼 클래스들을 준비합니다.
|
||||
@@ -151,25 +161,6 @@ namespace UVC.UI.Toolbar.View
|
||||
}
|
||||
if (layoutGroup == null) Debug.LogError("ToolbarView: layoutGroup이 할당되지 않았습니다.", this);
|
||||
|
||||
// 툴팁 표시에 필요한 루트 캔버스를 찾습니다.
|
||||
if (rootCanvas == null) rootCanvas = GetComponentInParent<Canvas>();
|
||||
if (rootCanvas == null)
|
||||
{
|
||||
Canvas[] canvases = FindObjectsByType<Canvas>(FindObjectsSortMode.None);
|
||||
foreach (Canvas c in canvases) { if (c.isRootCanvas) { rootCanvas = c; break; } }
|
||||
if (rootCanvas == null && canvases.Length > 0) rootCanvas = canvases[0];
|
||||
}
|
||||
|
||||
// 툴팁 매니저를 초기화합니다.
|
||||
if (rootCanvas != null)
|
||||
{
|
||||
if (!TooltipManager.Instance.IsInitialized) TooltipManager.Instance.Initialize(rootCanvas.transform, rootCanvas);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("ToolbarView: rootCanvas를 찾을 수 없어 TooltipManager를 초기화할 수 없습니다.");
|
||||
}
|
||||
|
||||
// 각 버튼 타입에 대한 뷰 프로세서를 등록합니다.
|
||||
_buttonViewProcessors[typeof(ToolbarStandardButton)] = new ToolbarStandardButtonViewProcessor();
|
||||
_buttonViewProcessors[typeof(ToolbarToggleButton)] = new ToolbarToggleButtonViewProcessor();
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace UVC.UI.Tooltip
|
||||
|
||||
private Coroutine _showTooltipCoroutine; // 툴팁 표시 지연을 위한 코루틴 참조
|
||||
private const float TooltipDelay = 0.5f; // 툴팁 표시까지의 지연 시간 (초 단위)
|
||||
private const float MouseMoveThreshold = 5f; // 마우스 이동 감지 임계값 (픽셀 단위)
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 포인터가 이 UI 요소의 영역 안으로 들어왔을 때 호출됩니다. (IPointerEnterHandler 인터페이스 구현)
|
||||
@@ -90,12 +91,36 @@ namespace UVC.UI.Tooltip
|
||||
/// 지정된 시간(TooltipDelay)만큼 대기한 후, OnPointerEnterAction을 호출하여 툴팁 표시를 요청하는 코루틴입니다.
|
||||
/// </summary>
|
||||
/// <param name="tooltip">표시할 툴팁 내용 또는 다국어 키입니다.</param>
|
||||
/// <param name="mousePosition">현재 마우스 포인터의 화면 좌표입니다.</param>
|
||||
private IEnumerator ShowTooltipAfterDelayCoroutine(string tooltip, Vector3 mousePosition)
|
||||
/// <param name="initialMousePosition">현재 마우스 포인터의 화면 좌표입니다.</param>
|
||||
private IEnumerator ShowTooltipAfterDelayCoroutine(string tooltip, Vector3 initialMousePosition)
|
||||
{
|
||||
yield return new WaitForSeconds(TooltipDelay); // 지정된 시간만큼 대기
|
||||
float stillTime = 0f; // 마우스가 움직이지 않은 시간
|
||||
Vector3 lastMousePosition = initialMousePosition;
|
||||
|
||||
while (stillTime < TooltipDelay)
|
||||
{
|
||||
// 현재 마우스 위치와 마지막 기록된 위치 간의 거리 계산
|
||||
float distance = Vector3.Distance(Input.mousePosition, lastMousePosition);
|
||||
|
||||
// 거리가 임계값을 초과하면 마우스가 움직인 것으로 간주
|
||||
if (distance > MouseMoveThreshold)
|
||||
{
|
||||
// 마우스가 움직였으므로 정지 시간을 리셋
|
||||
stillTime = 0f;
|
||||
lastMousePosition = Input.mousePosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 마우스가 거의 움직이지 않으면 정지 시간 증가
|
||||
stillTime += Time.unscaledDeltaTime;
|
||||
}
|
||||
|
||||
yield return null; // 다음 프레임까지 대기
|
||||
}
|
||||
|
||||
// TooltipDelay 시간 동안 마우스가 거의 움직이지 않았으면 툴팁 표시
|
||||
_showTooltipCoroutine = null; // 코루틴 완료 후 참조 null 처리
|
||||
OnPointerEnterAction?.Invoke(tooltip, mousePosition); // 연결된 액션 호출 (TooltipManager.HandlePointerEnter)
|
||||
OnPointerEnterAction?.Invoke(tooltip, Input.mousePosition); // 현재 마우스 위치 사용
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using TMPro;
|
||||
#nullable enable
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UVC.Locale;
|
||||
using UVC.Log; // LocalizationManager를 사용한다면 필요합니다.
|
||||
using UVC.util; // LocalizationManager를 사용한다면 필요합니다.
|
||||
|
||||
namespace UVC.UI.Tooltip
|
||||
{
|
||||
@@ -40,7 +41,7 @@ namespace UVC.UI.Tooltip
|
||||
#endregion
|
||||
|
||||
protected Transform _defaultParentTransform; // 툴팁 인스턴스가 생성될 기본 부모 Transform
|
||||
protected Canvas _rootCanvas; // 화면 좌표 계산 및 UI 스케일링에 사용될 Canvas
|
||||
protected Canvas canvas; // 화면 좌표 계산 및 UI 스케일링에 사용될 Canvas
|
||||
|
||||
protected GameObject _activeTooltipInstance; // 현재 활성화된 툴팁 게임 오브젝트
|
||||
protected TextMeshProUGUI _tooltipTextElement; // 툴팁 텍스트를 표시하는 TextMeshProUGUI 컴포넌트
|
||||
@@ -81,15 +82,15 @@ namespace UVC.UI.Tooltip
|
||||
/// void Start()
|
||||
/// {
|
||||
/// // mainCanvas.transform을 부모로, mainCanvas를 루트 캔버스로 하여 초기화
|
||||
/// TooltipManager.Instance.Initialize(mainCanvas.transform, mainCanvas);
|
||||
/// TooltipManager.Instance.Initialize(mainCanvas.transform);
|
||||
///
|
||||
/// // 특정 프리팹 경로를 사용하고 싶다면:
|
||||
/// // TooltipManager.Instance.Initialize(mainCanvas.transform, mainCanvas, "MyCustomTooltipPrefab");
|
||||
/// // TooltipManager.Instance.Initialize(mainCanvas.transform, "MyCustomTooltipPrefab");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public void Initialize(Transform defaultParent, Canvas rootCanvas, string tooltipPrefabPath = null)
|
||||
public void Initialize(Transform? defaultParent = null, string? tooltipPrefabPath = null)
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
@@ -97,17 +98,6 @@ namespace UVC.UI.Tooltip
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultParent == null)
|
||||
{
|
||||
Debug.LogError("TooltipVisualManager 초기화 실패: defaultParent가 null입니다.");
|
||||
return;
|
||||
}
|
||||
if (rootCanvas == null)
|
||||
{
|
||||
Debug.LogError("TooltipVisualManager 초기화 실패: rootCanvas가 null입니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(tooltipPrefabPath))
|
||||
{
|
||||
this.tooltipPrefabPath = tooltipPrefabPath; // 사용자 지정 경로가 제공되면 업데이트
|
||||
@@ -124,8 +114,11 @@ namespace UVC.UI.Tooltip
|
||||
return;
|
||||
}
|
||||
|
||||
canvas = CanvasUtil.GetOrCreate("ModalCanvas");
|
||||
defaultParent ??= canvas.transform; // 기본 부모가 null인 경우, 새로 생성한 Canvas의 Transform을 사용
|
||||
|
||||
_defaultParentTransform = defaultParent;
|
||||
_rootCanvas = rootCanvas;
|
||||
|
||||
|
||||
// 툴팁 인스턴스 생성 및 초기화
|
||||
_activeTooltipInstance = GameObject.Instantiate(tooltipPrefab, _defaultParentTransform);
|
||||
@@ -211,10 +204,10 @@ namespace UVC.UI.Tooltip
|
||||
_tooltipTextElement.text = text; // 텍스트 설정
|
||||
_activeTooltipInstance.SetActive(true); // 툴팁 활성화
|
||||
|
||||
// 툴팁을 현재 부모 내에서 가장 마지막 자식으로 만들어 다른 UI 요소들 위에 표시되도록 합니다.
|
||||
// 툴팁을 현재 부모 내에서 가장 첫번쨰 자식으로 만들어 다른 UI 요소들 위에 표시되도록 합니다.
|
||||
if (_activeTooltipInstance.transform.parent != null)
|
||||
{
|
||||
_activeTooltipInstance.transform.SetAsLastSibling();
|
||||
_activeTooltipInstance.transform.SetAsFirstSibling();
|
||||
}
|
||||
|
||||
_tooltipTextElement.ForceMeshUpdate(); // 텍스트 변경 후 메쉬 강제 업데이트 (정확한 크기 계산 위함)
|
||||
@@ -232,15 +225,15 @@ namespace UVC.UI.Tooltip
|
||||
/// <param name="mousePosition">현재 마우스 포인터의 화면 좌표입니다.</param>
|
||||
private void AdjustPosition(Vector3 mousePosition)
|
||||
{
|
||||
if (_rootCanvas == null || _tooltipRectTransform == null) return;
|
||||
if (canvas == null || _tooltipRectTransform == null) return;
|
||||
|
||||
Vector2 localPoint; // Canvas 내 로컬 좌표
|
||||
// 현재 Canvas의 Render Mode에 따라 적절한 카메라 사용
|
||||
Camera eventCamera = (_rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay) ? null : _rootCanvas.worldCamera;
|
||||
Camera eventCamera = (canvas.renderMode == RenderMode.ScreenSpaceOverlay) ? null : canvas.worldCamera;
|
||||
|
||||
// 화면 좌표(mousePosition)를 _rootCanvas의 RectTransform 내 로컬 좌표로 변환
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
_rootCanvas.transform as RectTransform, // 좌표 변환의 기준이 될 RectTransform
|
||||
canvas.transform as RectTransform, // 좌표 변환의 기준이 될 RectTransform
|
||||
mousePosition, // 변환할 화면 좌표
|
||||
eventCamera, // 이벤트 카메라 (ScreenSpaceOverlay의 경우 null)
|
||||
out localPoint // 변환된 로컬 좌표 결과
|
||||
@@ -278,17 +271,17 @@ namespace UVC.UI.Tooltip
|
||||
/// </summary>
|
||||
private void AdjustPositionWithinScreenBounds()
|
||||
{
|
||||
if (_tooltipRectTransform == null || _activeTooltipInstance == null || !_activeTooltipInstance.activeSelf || _rootCanvas == null) return;
|
||||
if (_tooltipRectTransform == null || _activeTooltipInstance == null || !_activeTooltipInstance.activeSelf || canvas == null) return;
|
||||
|
||||
Vector3[] tooltipCorners = new Vector3[4]; // 툴팁의 네 꼭짓점 월드 좌표
|
||||
_tooltipRectTransform.GetWorldCorners(tooltipCorners);
|
||||
|
||||
RectTransform canvasRectTransform = _rootCanvas.transform as RectTransform;
|
||||
RectTransform canvasRectTransform = canvas.transform as RectTransform;
|
||||
|
||||
// 화면 경계 좌표 설정
|
||||
float minX, maxX, minY, maxY;
|
||||
|
||||
if (_rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
// Screen Space Overlay 모드에서는 Screen.width/height를 사용
|
||||
minX = 0;
|
||||
@@ -308,7 +301,7 @@ namespace UVC.UI.Tooltip
|
||||
}
|
||||
|
||||
Vector3 currentPosition = _tooltipRectTransform.position; // 현재 툴팁 위치 (월드 좌표)
|
||||
Vector2 size = _tooltipRectTransform.sizeDelta * _rootCanvas.scaleFactor; // Canvas 스케일을 고려한 실제 픽셀 크기
|
||||
Vector2 size = _tooltipRectTransform.sizeDelta * canvas.scaleFactor; // Canvas 스케일을 고려한 실제 픽셀 크기
|
||||
Vector2 pivot = _tooltipRectTransform.pivot; // 툴팁의 Pivot
|
||||
|
||||
// 오른쪽 경계 넘어감: 왼쪽으로 이동
|
||||
@@ -326,8 +319,8 @@ namespace UVC.UI.Tooltip
|
||||
{
|
||||
Vector3 mouseWorldPos = Vector3.zero;
|
||||
// 마우스 포인터의 월드 Y 좌표를 가져옴
|
||||
if (_rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay) mouseWorldPos = Input.mousePosition;
|
||||
else RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRectTransform, Input.mousePosition, _rootCanvas.worldCamera, out mouseWorldPos);
|
||||
if (canvas.renderMode == RenderMode.ScreenSpaceOverlay) mouseWorldPos = Input.mousePosition;
|
||||
else RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRectTransform, Input.mousePosition, canvas.worldCamera, out mouseWorldPos);
|
||||
|
||||
// 마우스 Y 위치 + 툴팁 높이 (pivot 고려) + 약간의 오프셋
|
||||
currentPosition.y = mouseWorldPos.y + (size.y * (1 - pivot.y)) + 15f;
|
||||
@@ -355,7 +348,7 @@ namespace UVC.UI.Tooltip
|
||||
_tooltipTextElement = null;
|
||||
_tooltipRectTransform = null;
|
||||
_defaultParentTransform = null;
|
||||
_rootCanvas = null;
|
||||
canvas = null;
|
||||
_isInitialized = false;
|
||||
_instance = null; // 싱글톤 인스턴스 참조 해제
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user