Made the popup system a lot more generic

This commit is contained in:
Sebastian Bularca
2026-04-06 20:38:58 +02:00
parent fa7659d905
commit cfe202da44
21 changed files with 840 additions and 682 deletions

View File

@@ -7,6 +7,7 @@ using UnityEngine;
///
/// Use this approach for confirmation dialogs, tutorial tips, or any popup
/// that is triggered by game logic rather than hover events.
/// Also demonstrates PopupElementType variants and the generic Add() method.
/// </summary>
public class CodeOnlyPopupExample : MonoBehaviour {
[SerializeField] PopupSettings popupSettings;
@@ -14,6 +15,9 @@ public class CodeOnlyPopupExample : MonoBehaviour {
[SerializeField] Transform canvasRoot;
[SerializeField] RectTransform targetElement;
// Custom element type defined in game code
static readonly PopupElementType BadgeElement = new("badge");
IPopupSystem popupSystem;
void Start() {
@@ -24,12 +28,12 @@ public class CodeOnlyPopupExample : MonoBehaviour {
void Update() {
popupSystem?.Tick(Time.deltaTime);
// Show anchored to an element on key press
// Show anchored to an element
if(Input.GetKeyDown(KeyCode.Alpha1)) {
popupSystem.Show(PopupCategory.General, builder => {
builder
.AddHeader("Anchored Popup")
.AddText("This popup is anchored to a UI element.");
.AddText("Anchored Popup", PopupElementType.Header)
.AddText("This popup is anchored to a UI element.", PopupElementType.Text);
}, targetElement, AnchorSide.Right);
}
@@ -37,8 +41,8 @@ public class CodeOnlyPopupExample : MonoBehaviour {
if(Input.GetKeyDown(KeyCode.Alpha2)) {
popupSystem.ShowAtPosition(PopupCategory.General, builder => {
builder
.AddHeader("Fixed Position")
.AddText("This popup appears at the center of the screen.");
.AddText("Fixed Position", PopupElementType.Header)
.AddText("This popup appears at the center of the screen.", PopupElementType.Text);
}, new Vector2(Screen.width * 0.5f, Screen.height * 0.5f));
}
@@ -46,12 +50,36 @@ public class CodeOnlyPopupExample : MonoBehaviour {
if(Input.GetKeyDown(KeyCode.Alpha3)) {
popupSystem.Show(PopupCategory.General, builder => {
builder
.AddHeader("Follow Mouse")
.AddText("This popup follows the cursor.");
.AddText("Follow Mouse", PopupElementType.Header)
.AddText("This popup follows the cursor.", PopupElementType.Text);
});
}
// Hide on key press
// Demonstrate variant elements
// Requires "header_gold" and "separator_thick" entries in PopupSettings.elementPrefabs
if(Input.GetKeyDown(KeyCode.Alpha4)) {
popupSystem.Show(PopupCategory.General, builder => {
builder
.AddText("Legendary Item", PopupElementType.Header.Variant("gold"))
.AddSeparator(PopupElementType.Separator.Variant("thick"))
.AddNameValue("Damage", "150", PopupElementType.LabelValueText)
.AddText("A weapon forged in dragon fire.", "FF6600", PopupElementType.Text);
}, targetElement, AnchorSide.Below);
}
// Demonstrate generic Add() for fully custom elements
// Requires a "badge" entry in PopupSettings.elementPrefabs
if(Input.GetKeyDown(KeyCode.Alpha5)) {
popupSystem.Show(PopupCategory.General, builder => {
builder.AddText("Custom Element", PopupElementType.Header);
var badge = builder.Add(BadgeElement);
if(badge != null) {
// Access any components on the custom prefab
// badge.GetComponent<MyBadgeComponent>().SetData(...);
}
}, targetElement);
}
if(Input.GetKeyDown(KeyCode.Escape)) {
popupSystem.HideAll();
}

View File

@@ -6,7 +6,9 @@ using UnityEngine;
/// Example: Using PopupSystem with dynamically instantiated UI elements.
///
/// When UI elements are created at runtime (e.g. inventory slots, party member portraits),
/// use InitializeTriggersInChildren to scan and bind triggers after instantiation.
/// use InitializeTriggersInChildren to scan, bind, and configure triggers after instantiation.
/// The callback receives both the trigger (MonoBehaviour, for hierarchy queries) and the
/// view (behavior handler, for setting content).
/// </summary>
public class DynamicTriggersExample : MonoBehaviour {
[SerializeField] PopupSettings popupSettings;
@@ -21,21 +23,17 @@ public class DynamicTriggersExample : MonoBehaviour {
popupSystem = new PopupSystem(popupSettings, popupReferencePrefab, canvasRoot);
popupSystem.RegisterCategory(PopupCategory.Item, priority: 5);
// Simulate creating dynamic UI slots
for(int i = 0; i < 5; i++) {
Instantiate(slotPrefab, slotsContainer);
}
// Scan the container for any PopupTrigger components on the new slots.
// Each trigger is automatically bound to the popup system.
// The configure callback lets you set content per trigger.
popupSystem.InitializeTriggersInChildren(slotsContainer, trigger => {
popupSystem.InitializeTriggersInChildren(slotsContainer, (trigger, view) => {
var slotName = trigger.gameObject.name;
trigger.SetContent(builder => {
view.SetContent(builder => {
builder
.AddHeader(slotName)
.AddSeparator()
.AddText("This is a dynamically created slot.");
.AddText(slotName, PopupElementType.Header)
.AddSeparator(PopupElementType.Separator)
.AddText("This is a dynamically created slot.", PopupElementType.Text);
});
});
}

View File

@@ -9,7 +9,10 @@ using UnityEngine;
/// 2. Assign the PopupSettings asset and PopupReference prefab.
/// 3. Set canvasRoot to the root Canvas that contains your UI and PopupTrigger components.
/// 4. Place PopupTrigger components on any UI elements that should show popups on hover.
/// 5. The system auto-scans triggers on creation. Use SetContent() to provide data.
/// 5. The system auto-scans triggers on creation. Use GetTriggerHandler() to set content.
///
/// Element prefabs are configured in PopupSettings via PopupElementType.
/// Add entries mapping types (header, text, label_value_text, separator, image) to prefabs.
/// </summary>
public class PopupSystemExample : MonoBehaviour {
[SerializeField] PopupSettings popupSettings;
@@ -19,52 +22,46 @@ public class PopupSystemExample : MonoBehaviour {
IPopupSystem popupSystem;
void Start() {
// Create the system. Passing canvasRoot auto-scans all PopupTrigger components.
popupSystem = new PopupSystem(popupSettings, popupReferencePrefab, canvasRoot);
// Register categories before showing popups.
popupSystem.RegisterCategory(PopupCategory.Character, priority: 10);
popupSystem.RegisterCategory(PopupCategory.Item, priority: 5);
popupSystem.RegisterCategory(PopupCategory.General, priority: 1);
// Option A: Set content on an auto-scanned trigger by GameObject name.
var characterTrigger = popupSystem.GetTrigger("CharacterPortrait");
if(characterTrigger != null) {
characterTrigger.SetContent(builder => {
builder
.AddHeader("Kael")
.AddText("Human Warrior", "CCCCCC")
.AddSeparator()
.AddStat("Health", 55)
.AddStat("Mana", 42)
.AddStat("Level", 1)
.AddSeparator()
.AddStat("Might", 8)
.AddStat("Reflex", 2)
.AddStat("Knowledge", 5)
.AddStat("Perception", 1);
});
}
// Set content on an auto-scanned trigger by GameObject name.
var characterHandler = popupSystem.GetTriggerHandler("CharacterPortrait");
characterHandler?.SetContent(builder => {
builder
.AddText("Kael", PopupElementType.Header)
.AddText("Human Warrior", "CCCCCC", PopupElementType.Text)
.AddSeparator(PopupElementType.Separator)
.AddNameValue("Health", 55, PopupElementType.LabelValueText)
.AddNameValue("Mana", 42, PopupElementType.LabelValueText)
.AddNameValue("Level", 1, PopupElementType.LabelValueText)
.AddSeparator(PopupElementType.Separator)
.AddNameValue("Might", 8, PopupElementType.LabelValueText)
.AddNameValue("Reflex", 2, PopupElementType.LabelValueText)
.AddNameValue("Knowledge", 5, PopupElementType.LabelValueText)
.AddNameValue("Perception", 1, PopupElementType.LabelValueText);
});
// Option B: Set content on all triggers of a category.
foreach(var trigger in popupSystem.GetTriggers(PopupCategory.Item)) {
trigger.SetContent(builder => {
// Set content on all triggers of a category.
foreach(var handler in popupSystem.GetTriggerHandlers(PopupCategory.Item)) {
handler.SetContent(builder => {
builder
.AddHeader("Health Potion")
.AddSeparator()
.AddText("Restores a moderate amount of health.")
.AddStat("Heal Amount", 50);
.AddText("Health Potion", PopupElementType.Header)
.AddSeparator(PopupElementType.Separator)
.AddText("Restores a moderate amount of health.", PopupElementType.Text)
.AddNameValue("Heal Amount", 50, PopupElementType.LabelValueText);
});
}
}
void Update() {
// Tick drives delay timers and animations.
popupSystem?.Tick(Time.deltaTime);
}
void OnDestroy() {
// Clean up all popup views.
popupSystem?.Dispose();
}
}