using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.UI;
namespace Watermelon
{
///
/// Null-safe extension helpers for and ,
/// plus snapping utilities.
///
/// The KillActive / CompleteActive family is the recommended way to stop tweens
/// stored in fields, because they handle both the null case (tween never started or returned null
/// from ) and the already-completed case without requiring extra null checks.
///
public static class TweenExtensions
{
/// Returns true if is not null and is currently active.
public static bool ExistsAndActive(this TweenCase tweenCase)
{
return tweenCase != null && tweenCase.IsActive;
}
///
/// Kills if it is not null and is currently active.
/// Returns true if the tween was killed, false if it was already inactive or null.
///
public static bool KillActive(this TweenCase tweenCase)
{
if (tweenCase != null && tweenCase.IsActive)
{
tweenCase.Kill();
return true;
}
return false;
}
/// Kills every active tween in , skipping null entries and already-inactive tweens.
public static void KillActive(this TweenCase[] tweenCases)
{
if(tweenCases != null)
{
foreach (TweenCase tweenCase in tweenCases)
{
if (tweenCase != null && tweenCase.IsActive)
{
tweenCase.Kill();
}
}
}
}
///
/// Kills collection if it is not null and is not yet complete.
/// Returns true if the collection was killed.
///
public static bool KillActive(this TweenCaseCollection tweenCase)
{
if (tweenCase != null && !tweenCase.IsComplete())
{
tweenCase.Kill();
return true;
}
return false;
}
///
/// Completes if it is not null and has not yet finished.
/// Returns true if the tween was completed.
///
public static bool CompleteActive(this TweenCase tweenCase)
{
if (tweenCase != null && !tweenCase.IsCompleted)
{
tweenCase.Complete();
return true;
}
return false;
}
/// Forces immediate completion of every active tween in , skipping null entries.
public static void CompleteActive(this TweenCase[] tweenCases)
{
if (tweenCases != null)
{
foreach (TweenCase tweenCase in tweenCases)
{
if (tweenCase != null && tweenCase.IsActive)
{
tweenCase.Complete();
}
}
}
}
///
/// Smoothly scrolls the so that the bottom edge of
/// is visible within the viewport.
///
/// The scroll rect to animate.
/// The child to bring into view.
/// Scroll animation duration in seconds.
/// Additional horizontal offset applied to the target position.
/// Additional vertical offset applied to the target position.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TweenCase DoSnapTargetBottom(this ScrollRect scrollRect, RectTransform target, float duration, float offsetX = 0, float offsetY = 0)
{
var targetPosition = target.position + Vector3.up * (scrollRect.viewport.rect.height / 2 + target.sizeDelta.y);
return scrollRect.SnapToTarget(targetPosition, duration, offsetX, offsetY);
}
///
/// Smoothly scrolls the so that the top edge of
/// is aligned with the top of the viewport.
///
/// The scroll rect to animate.
/// The child to bring into view.
/// Scroll animation duration in seconds.
/// Additional horizontal offset applied to the target position.
/// Additional vertical offset applied to the target position.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TweenCase DoSnapTargetTop(this ScrollRect scrollRect, RectTransform target, float duration, float offsetX = 0, float offsetY = 0)
{
return scrollRect.SnapToTarget(target.position, duration, offsetX, offsetY);
}
///
/// Smoothly scrolls the so that a world-space position
/// is visible, respecting the scroll rect's horizontal/vertical constraints.
///
/// The scroll rect to animate.
/// World-space position to scroll towards.
/// Scroll animation duration in seconds.
/// Additional horizontal offset in viewport space.
/// Additional vertical offset in viewport space.
public static TweenCase SnapToTarget(this ScrollRect scrollRect, Vector3 target, float duration, float offsetX = 0, float offsetY = 0)
{
Vector2 contentPosition = scrollRect.viewport.InverseTransformPoint(scrollRect.content.position);
Vector2 newPosition = scrollRect.viewport.InverseTransformPoint(target);
newPosition = new Vector2(newPosition.x + offsetX, newPosition.y + offsetY);
if (!scrollRect.horizontal)
newPosition.x = contentPosition.x;
if (!scrollRect.vertical)
newPosition.y = contentPosition.y;
return scrollRect.content.DOAnchoredPosition(contentPosition - newPosition, duration);
}
}
}