forked from Shardstone/trail-into-darkness
added a disfunctionalk popup system
This commit is contained in:
104
Packages/com.jovian.popup-system/Editor/PopupCategoryDrawer.cs
Normal file
104
Packages/com.jovian.popup-system/Editor/PopupCategoryDrawer.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.PopupSystem.Editor {
|
||||
[CustomPropertyDrawer(typeof(PopupCategory))]
|
||||
public sealed class PopupCategoryDrawer : PropertyDrawer {
|
||||
private static readonly string[] builtInIds = {
|
||||
"Character",
|
||||
"Item",
|
||||
"Skill",
|
||||
"General"
|
||||
};
|
||||
|
||||
private const string customLabel = "Custom...";
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
|
||||
var idProp = FindIdProperty(property);
|
||||
if(idProp == null) {
|
||||
return EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
var currentValue = idProp.stringValue ?? "";
|
||||
var isCustom = Array.IndexOf(builtInIds, currentValue) < 0;
|
||||
|
||||
// Two lines when in custom mode: dropdown + text field
|
||||
return isCustom
|
||||
? EditorGUIUtility.singleLineHeight * 2 + 2
|
||||
: EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
|
||||
EditorGUI.BeginProperty(position, label, property);
|
||||
|
||||
var idProp = FindIdProperty(property);
|
||||
if(idProp == null) {
|
||||
EditorGUI.LabelField(position, label.text, "Cannot resolve PopupCategory id field");
|
||||
EditorGUI.EndProperty();
|
||||
return;
|
||||
}
|
||||
|
||||
var currentValue = idProp.stringValue ?? "";
|
||||
var builtInIndex = Array.IndexOf(builtInIds, currentValue);
|
||||
var isCustom = builtInIndex < 0 && !string.IsNullOrEmpty(currentValue);
|
||||
|
||||
// Build display options: built-in entries + "Custom..."
|
||||
var options = new List<string>(builtInIds);
|
||||
options.Add(customLabel);
|
||||
|
||||
// Determine selected index
|
||||
int selectedIndex;
|
||||
if(builtInIndex >= 0) {
|
||||
selectedIndex = builtInIndex;
|
||||
}
|
||||
else {
|
||||
selectedIndex = options.Count - 1; // "Custom..."
|
||||
}
|
||||
|
||||
// First line: dropdown
|
||||
var dropdownRect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
|
||||
var newIndex = EditorGUI.Popup(dropdownRect, label.text, selectedIndex, options.ToArray());
|
||||
|
||||
if(newIndex != selectedIndex) {
|
||||
if(newIndex < builtInIds.Length) {
|
||||
idProp.stringValue = builtInIds[newIndex];
|
||||
}
|
||||
else {
|
||||
// Switched to custom — seed with a placeholder if currently built-in
|
||||
if(!isCustom) {
|
||||
idProp.stringValue = "NewCategory";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second line: editable text field when custom
|
||||
var finalIsCustom = newIndex >= builtInIds.Length || (newIndex == selectedIndex && isCustom);
|
||||
if(finalIsCustom) {
|
||||
var textRect = new Rect(
|
||||
position.x + EditorGUIUtility.labelWidth + 2,
|
||||
position.y + EditorGUIUtility.singleLineHeight + 2,
|
||||
position.width - EditorGUIUtility.labelWidth - 2,
|
||||
EditorGUIUtility.singleLineHeight);
|
||||
var newValue = EditorGUI.TextField(textRect, idProp.stringValue);
|
||||
if(newValue != idProp.stringValue) {
|
||||
idProp.stringValue = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
|
||||
private static SerializedProperty FindIdProperty(SerializedProperty property) {
|
||||
// readonly struct with [SerializeField] readonly string id
|
||||
// Unity serializes this as "id" directly
|
||||
var prop = property.FindPropertyRelative("id");
|
||||
if(prop != null) {
|
||||
return prop;
|
||||
}
|
||||
// Fallback: auto-property backing field pattern
|
||||
return property.FindPropertyRelative("<id>k__BackingField");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cc96a089d31fe14cbadf6c59ea0c9aa
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using Jovian.PopupSystem.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Jovian.PopupSystem {
|
||||
public interface IPopupSystem {
|
||||
void InitializeTriggersInChildren(Transform parent, Action<PopupTrigger> configureTrigger);
|
||||
void RegisterCategory(PopupCategory category, int priority = 0);
|
||||
void Show(PopupCategory category, Action<PopupContentBuilder> buildContent,
|
||||
RectTransform anchor = null, AnchorSide? anchorSide = null);
|
||||
|
||||
@@ -3,8 +3,8 @@ using UnityEngine;
|
||||
|
||||
namespace Jovian.PopupSystem {
|
||||
[Serializable]
|
||||
public readonly struct PopupCategory : IEquatable<PopupCategory> {
|
||||
[SerializeField] readonly string id;
|
||||
public struct PopupCategory : IEquatable<PopupCategory> {
|
||||
[SerializeField] string id;
|
||||
|
||||
public string Id => id;
|
||||
|
||||
|
||||
@@ -9,11 +9,13 @@ namespace Jovian.PopupSystem {
|
||||
readonly PopupSettings settings;
|
||||
readonly PopupReference viewPrefab;
|
||||
readonly Func<IPopupAnimator> animatorFactory;
|
||||
readonly Transform canvasParent;
|
||||
readonly Dictionary<PopupCategory, ViewState> categories = new();
|
||||
|
||||
public PopupSystem(PopupSettings settings, PopupReference viewPrefab, Func<IPopupAnimator> animatorFactory = null) {
|
||||
public PopupSystem(PopupSettings settings, PopupReference viewPrefab, Transform canvasParent = null, Func<IPopupAnimator> animatorFactory = null) {
|
||||
this.settings = settings;
|
||||
this.viewPrefab = viewPrefab;
|
||||
this.canvasParent = canvasParent;
|
||||
this.animatorFactory = animatorFactory ?? (() => new FadePopupAnimator());
|
||||
}
|
||||
|
||||
@@ -110,6 +112,13 @@ namespace Jovian.PopupSystem {
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeTriggersInChildren(Transform parent, Action<PopupTrigger> configureTrigger) {
|
||||
var triggers = parent.GetComponentsInChildren<PopupTrigger>(true);
|
||||
foreach(var trigger in triggers) {
|
||||
configureTrigger(trigger);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
foreach(var kvp in categories) {
|
||||
if(kvp.Value.view != null) {
|
||||
@@ -148,14 +157,24 @@ namespace Jovian.PopupSystem {
|
||||
if(state.view != null) {
|
||||
return;
|
||||
}
|
||||
state.view = Object.Instantiate(viewPrefab);
|
||||
state.view.SetVisible(false);
|
||||
state.view.SetMaxWidth(settings.maxPopupWidth);
|
||||
|
||||
if(canvasParent != null) {
|
||||
// Parent under existing scene Canvas — nested Canvas inherits CanvasScaler
|
||||
state.view = Object.Instantiate(viewPrefab, canvasParent);
|
||||
}
|
||||
else {
|
||||
state.view = Object.Instantiate(viewPrefab);
|
||||
}
|
||||
|
||||
// Configure Canvas as override sorting so it renders on top
|
||||
var canvas = state.view.GetComponent<Canvas>();
|
||||
if(canvas != null) {
|
||||
canvas.overrideSorting = true;
|
||||
canvas.sortingOrder = settings.sortingOrder;
|
||||
}
|
||||
|
||||
state.view.SetVisible(false);
|
||||
state.view.SetMaxWidth(settings.maxPopupWidth);
|
||||
}
|
||||
|
||||
private void DismissLowerPriority(int showingPriority) {
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Jovian.PopupSystem.UI {
|
||||
float maxWidth;
|
||||
bool isVisible;
|
||||
bool layoutDirty;
|
||||
Canvas rootCanvas;
|
||||
Camera canvasCamera;
|
||||
|
||||
public CanvasGroup CanvasGroup => canvasGroup;
|
||||
public RectTransform Content => content;
|
||||
@@ -172,9 +174,20 @@ namespace Jovian.PopupSystem.UI {
|
||||
}
|
||||
}
|
||||
|
||||
private void CacheCanvas() {
|
||||
if(rootCanvas != null) {
|
||||
return;
|
||||
}
|
||||
rootCanvas = GetComponentInParent<Canvas>()?.rootCanvas;
|
||||
canvasCamera = rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay
|
||||
? rootCanvas.worldCamera : null;
|
||||
}
|
||||
|
||||
private void PositionAnchoredTo(RectTransform target, AnchorSide side) {
|
||||
var targetRect = GetScreenRect(target);
|
||||
var popupSize = GetScreenRect((RectTransform)transform).size;
|
||||
CacheCanvas();
|
||||
var targetRect = GetScreenRect(target, canvasCamera);
|
||||
var popupRect = GetScreenRect((RectTransform)transform, canvasCamera);
|
||||
var popupSize = popupRect.size;
|
||||
|
||||
var pos = side switch {
|
||||
AnchorSide.Below => new Vector2(targetRect.center.x - popupSize.x * 0.5f, targetRect.yMin - popupSize.y),
|
||||
@@ -188,26 +201,36 @@ namespace Jovian.PopupSystem.UI {
|
||||
}
|
||||
|
||||
private void PositionAtScreenPoint(Vector2 screenPos) {
|
||||
CacheCanvas();
|
||||
var rt = (RectTransform)transform;
|
||||
if(layoutDirty) {
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(content);
|
||||
layoutDirty = false;
|
||||
}
|
||||
var popupSize = rt.rect.size;
|
||||
var popupSize = GetScreenRect(rt, canvasCamera).size;
|
||||
|
||||
// Clamp to screen
|
||||
screenPos.x = Mathf.Clamp(screenPos.x, screenEdgePadding, Screen.width - popupSize.x - screenEdgePadding);
|
||||
screenPos.y = Mathf.Clamp(screenPos.y, screenEdgePadding, Screen.height - popupSize.y - screenEdgePadding);
|
||||
|
||||
rt.position = screenPos;
|
||||
// Convert screen position to parent local space
|
||||
var parentRt = rt.parent as RectTransform;
|
||||
if(parentRt != null) {
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRt, screenPos, canvasCamera, out var localPos);
|
||||
rt.localPosition = localPos;
|
||||
}
|
||||
else {
|
||||
rt.position = screenPos;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Vector3[] cornersBuffer = new Vector3[4];
|
||||
|
||||
private static Rect GetScreenRect(RectTransform rt) {
|
||||
private static Rect GetScreenRect(RectTransform rt, Camera camera) {
|
||||
rt.GetWorldCorners(cornersBuffer);
|
||||
var min = new Vector2(cornersBuffer[0].x, cornersBuffer[0].y);
|
||||
var max = new Vector2(cornersBuffer[2].x, cornersBuffer[2].y);
|
||||
// Convert world corners to screen space
|
||||
var min = RectTransformUtility.WorldToScreenPoint(camera, cornersBuffer[0]);
|
||||
var max = RectTransformUtility.WorldToScreenPoint(camera, cornersBuffer[2]);
|
||||
return new Rect(min, max - min);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user