forked from Shardstone/trail-into-darkness
Added a popup system
This commit is contained in:
187
Packages/com.jovian.popup-system/Runtime/PopupSystem.cs
Normal file
187
Packages/com.jovian.popup-system/Runtime/PopupSystem.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Jovian.PopupSystem.UI;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Jovian.PopupSystem {
|
||||
public sealed class PopupSystem : IPopupSystem {
|
||||
readonly PopupSettings settings;
|
||||
readonly PopupView viewPrefab;
|
||||
readonly Func<IPopupAnimator> animatorFactory;
|
||||
readonly Dictionary<PopupCategory, ViewState> categories = new();
|
||||
|
||||
public PopupSystem(PopupSettings settings, PopupView viewPrefab, Func<IPopupAnimator> animatorFactory = null) {
|
||||
this.settings = settings;
|
||||
this.viewPrefab = viewPrefab;
|
||||
this.animatorFactory = animatorFactory ?? (() => new FadePopupAnimator());
|
||||
}
|
||||
|
||||
public void RegisterCategory(PopupCategory category, int priority = 0) {
|
||||
if(categories.ContainsKey(category)) {
|
||||
return;
|
||||
}
|
||||
var effectivePriority = settings.GetPriority(category);
|
||||
if(effectivePriority == 0) {
|
||||
effectivePriority = priority;
|
||||
}
|
||||
categories[category] = new ViewState {
|
||||
priority = effectivePriority,
|
||||
delay = settings.GetDelay(category),
|
||||
animator = animatorFactory()
|
||||
};
|
||||
}
|
||||
|
||||
public void Show(PopupCategory category, Action<PopupContentBuilder> buildContent,
|
||||
RectTransform anchor = null, AnchorSide? anchorSide = null) {
|
||||
if(!categories.TryGetValue(category, out var state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DismissLowerPriority(state.priority);
|
||||
|
||||
state.pendingBuild = buildContent;
|
||||
state.pendingAnchor = anchor;
|
||||
state.pendingAnchorSide = anchorSide ?? settings.defaultAnchorSide;
|
||||
state.pendingScreenPos = null;
|
||||
state.delayTimer = state.delay;
|
||||
state.isPending = true;
|
||||
}
|
||||
|
||||
public void ShowAtPosition(PopupCategory category, Action<PopupContentBuilder> buildContent,
|
||||
Vector2 screenPosition) {
|
||||
if(!categories.TryGetValue(category, out var state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DismissLowerPriority(state.priority);
|
||||
|
||||
state.pendingBuild = buildContent;
|
||||
state.pendingAnchor = null;
|
||||
state.pendingScreenPos = screenPosition;
|
||||
state.delayTimer = state.delay;
|
||||
state.isPending = true;
|
||||
}
|
||||
|
||||
public void Hide(PopupCategory category) {
|
||||
if(!categories.TryGetValue(category, out var state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.isPending = false;
|
||||
if(state.view != null && state.view.IsVisible) {
|
||||
state.animator.Hide(state.view.CanvasGroup, settings.fadeDuration, () => {
|
||||
state.view.SetVisible(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void HideAll() {
|
||||
foreach(var kvp in categories) {
|
||||
Hide(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime) {
|
||||
foreach(var kvp in categories) {
|
||||
kvp.Value.animator.Tick(deltaTime);
|
||||
}
|
||||
|
||||
foreach(var kvp in categories) {
|
||||
var state = kvp.Value;
|
||||
if(!state.isPending) {
|
||||
continue;
|
||||
}
|
||||
|
||||
state.delayTimer -= deltaTime;
|
||||
if(state.delayTimer > 0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
state.isPending = false;
|
||||
ShowImmediate(state);
|
||||
}
|
||||
|
||||
foreach(var kvp in categories) {
|
||||
var state = kvp.Value;
|
||||
if(state.view != null && state.view.IsVisible && state.isFollowMouse) {
|
||||
state.view.UpdatePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
foreach(var kvp in categories) {
|
||||
if(kvp.Value.view != null) {
|
||||
Object.Destroy(kvp.Value.view.gameObject);
|
||||
}
|
||||
}
|
||||
categories.Clear();
|
||||
}
|
||||
|
||||
private void ShowImmediate(ViewState state) {
|
||||
EnsureView(state);
|
||||
state.view.ClearContent();
|
||||
|
||||
var builder = new PopupContentBuilder(state.view);
|
||||
state.pendingBuild?.Invoke(builder);
|
||||
|
||||
if(state.pendingScreenPos.HasValue) {
|
||||
state.view.SetFixedPosition(state.pendingScreenPos.Value, settings.screenEdgePadding);
|
||||
state.isFollowMouse = false;
|
||||
}
|
||||
else if(state.pendingAnchor != null) {
|
||||
state.view.SetAnchorMode(state.pendingAnchor, state.pendingAnchorSide, settings.screenEdgePadding);
|
||||
state.isFollowMouse = false;
|
||||
}
|
||||
else {
|
||||
state.view.SetFollowMouseMode(settings.followMouseOffset, settings.screenEdgePadding);
|
||||
state.isFollowMouse = true;
|
||||
}
|
||||
|
||||
state.view.SetVisible(true);
|
||||
state.view.UpdatePosition();
|
||||
state.animator.Show(state.view.CanvasGroup, settings.fadeDuration, null);
|
||||
}
|
||||
|
||||
private void EnsureView(ViewState state) {
|
||||
if(state.view != null) {
|
||||
return;
|
||||
}
|
||||
state.view = Object.Instantiate(viewPrefab);
|
||||
state.view.SetVisible(false);
|
||||
state.view.SetMaxWidth(settings.maxPopupWidth);
|
||||
|
||||
var canvas = state.view.GetComponent<Canvas>();
|
||||
if(canvas != null) {
|
||||
canvas.sortingOrder = settings.sortingOrder;
|
||||
}
|
||||
}
|
||||
|
||||
private void DismissLowerPriority(int showingPriority) {
|
||||
foreach(var kvp in categories) {
|
||||
var state = kvp.Value;
|
||||
if(state.priority < showingPriority && state.view != null && state.view.IsVisible) {
|
||||
state.isPending = false;
|
||||
state.animator.Hide(state.view.CanvasGroup, settings.fadeDuration, () => {
|
||||
state.view.SetVisible(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ViewState {
|
||||
public PopupView view;
|
||||
public IPopupAnimator animator;
|
||||
public int priority;
|
||||
public float delay;
|
||||
public float delayTimer;
|
||||
public bool isPending;
|
||||
public bool isFollowMouse;
|
||||
public Action<PopupContentBuilder> pendingBuild;
|
||||
public RectTransform pendingAnchor;
|
||||
public AnchorSide pendingAnchorSide;
|
||||
public Vector2? pendingScreenPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user