using System; using Cysharp.Threading.Tasks; using SepCore.AsyncTask; using SepCore.Event; using UnityEngine; using UnityGameFramework.Runtime; namespace SepCore.UI { public partial class ItemTooltipForm : UGuiForm { [SerializeField] private bool _autoResizeHeight = true; [SerializeField] private float _fixedTopHeight = 150f; [SerializeField] private float _fixedBottomHeight = 150f; [SerializeField] private float _fixedWidth = 300f; [SerializeField] private float _screenEdgePadding = 0f; private ItemTooltipContext _context; private Vector3 _targetPos; private bool _listenersBound; public async UniTaskVoid RefreshUI(ItemTooltipContext context) { EnsureListeners(); _context = context; _targetPos = ConvertWorldToAnchored(_context.TargetPos); View.contentRectTransform.anchoredPosition3D = _targetPos; View.titleText.text = _context.Title; View.typeText.text = BuildTypeText(_context.ItemType); View.infoText.text = _context.Description; View.textFormatText.Set(_context.Price); View.recycleButton.gameObject.SetActive(_context.ShowRecycleButton); if (!string.IsNullOrEmpty(_context.IconAssetName)) { string iconAssetName = _context.IconAssetName; var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName); if (_context != null && _context.IconAssetName == iconAssetName) { View.iconArea.OnInit(sprite, context.Rarity); ResizeToFitContent(); ApplyClampedTargetPos(); } } ResizeToFitContent(); ApplyClampedTargetPos(); } protected override void OnOpen(object userData) { base.OnOpen(userData); if (userData is not ItemTooltipContext context) { Log.Error("ItemTooltipForm context is invalid."); return; } RefreshUI(context).Forget(); } protected override void OnClose(bool isShutdown, object userData) { _context = null; base.OnClose(isShutdown, userData); } public void OnRecycleButtonClick() { GameEntry.Event.Fire(this, ItemTooltipRecycleEventArgs.Create(_context.Index, _context.Price)); } public void OnCancelButtonClick() { GameEntry.Event.Fire(this, ItemTooltipHideEventArgs.Create()); } private void EnsureListeners() { if (_listenersBound) { return; } _listenersBound = true; View.recycleButton.onClick.AddListener(OnRecycleButtonClick); View.cancelButton.onClick.AddListener(OnCancelButtonClick); } private void ResizeToFitContent() { if (!_autoResizeHeight) { return; } View.infoText.ForceMeshUpdate(); var descriptionSize = View.infoText.rectTransform.sizeDelta; descriptionSize.y = View.infoText.preferredHeight; View.infoText.rectTransform.sizeDelta = descriptionSize; float descriptionHeight = Mathf.Max(0f, View.infoText.preferredHeight); float targetHeight = _fixedTopHeight + descriptionHeight + _fixedBottomHeight; Vector2 size = new Vector2(_fixedWidth, targetHeight); View.contentRectTransform.sizeDelta = size; } private void ApplyClampedTargetPos() { RectTransform parent = View.contentRectTransform.parent as RectTransform; if (parent == null) { return; } Vector2 size = View.contentRectTransform.sizeDelta; if (size.x <= 0f || size.y <= 0f) { size = View.contentRectTransform.rect.size; } Vector2 pivot = View.contentRectTransform.pivot; float halfWidth = parent.rect.width * 0.5f; float halfHeight = parent.rect.height * 0.5f; float minX = -halfWidth + size.x * pivot.x + _screenEdgePadding; float maxX = halfWidth - size.x * (1f - pivot.x) - _screenEdgePadding; float minY = -halfHeight + size.y * pivot.y + _screenEdgePadding; float maxY = halfHeight - size.y * (1f - pivot.y) - _screenEdgePadding; float clampedX = minX <= maxX ? Mathf.Clamp(_targetPos.x, minX, maxX) : 0f; float clampedY = minY <= maxY ? Mathf.Clamp(_targetPos.y, minY, maxY) : 0f; View.contentRectTransform.anchoredPosition3D = new Vector3(clampedX, clampedY, _targetPos.z); } private Vector3 ConvertWorldToAnchored(Vector3 worldPos) { RectTransform parent = View.contentRectTransform.parent as RectTransform; if (parent == null) { return worldPos; } Canvas canvas = View.contentRectTransform.GetComponentInParent(); Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null; Camera uiCamera = null; if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay) { uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main; } Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPos); if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parent, screenPoint, uiCamera, out Vector2 localPoint)) { return worldPos; } float z = View.contentRectTransform.anchoredPosition3D.z; return new Vector3(localPoint.x, localPoint.y, z); } private string BuildTypeText(ItemType type) { return type switch { ItemType.None => "error", ItemType.Weapon => "武器", ItemType.Prop => "道具", _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; } } }