mirror of
https://github.com/RHeavenStudio/HeavenStudio.git
synced 2025-06-12 11:27:39 +02:00
Editor stuff
This commit is contained in:
@ -0,0 +1,303 @@
|
||||
/// Credit setchi (https://github.com/setchi)
|
||||
/// Sourced from - https://github.com/setchi/FancyScrollView
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UI.Extensions.EasingCore;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
|
||||
/// 無限スクロールおよびスナップには対応していません.
|
||||
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
|
||||
/// 代わりに <see cref="FancyScrollRect{TItemData}"/> を使用します.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
|
||||
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
|
||||
[RequireComponent(typeof(Scroller))]
|
||||
public abstract class FancyScrollRect<TItemData, TContext> : FancyScrollView<TItemData, TContext>
|
||||
where TContext : class, IFancyScrollRectContext, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// スクロール中にセルが再利用されるまでの余白のセル数.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <c>0</c> を指定するとセルが完全に隠れた直後に再利用されます.
|
||||
/// <c>1</c> 以上を指定すると, そのセル数だけ余分にスクロールしてから再利用されます.
|
||||
/// </remarks>
|
||||
[SerializeField] protected float reuseCellMarginCount = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// コンテンツ先頭の余白.
|
||||
/// </summary>
|
||||
[SerializeField] protected float paddingHead = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// コンテンツ末尾の余白.
|
||||
/// </summary>
|
||||
[SerializeField] protected float paddingTail = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// スクロール軸方向のセル同士の余白.
|
||||
/// </summary>
|
||||
[SerializeField] protected float spacing = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// セルのサイズ.
|
||||
/// </summary>
|
||||
protected abstract float CellSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// スクロール可能かどうか.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// アイテム数が十分少なくビューポート内に全てのセルが収まっている場合は <c>false</c>, それ以外は <c>true</c> になります.
|
||||
/// </remarks>
|
||||
protected virtual bool Scrollable => MaxScrollPosition > 0f;
|
||||
|
||||
Scroller cachedScroller;
|
||||
|
||||
/// <summary>
|
||||
/// スクロール位置を制御する <see cref="FancyScrollView.Scroller"/> のインスタンス.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Scroller"/> のスクロール位置を変更する際は必ず <see cref="ToScrollerPosition(float)"/> を使用して変換した位置を使用してください.
|
||||
/// </remarks>
|
||||
protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent<Scroller>());
|
||||
|
||||
float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f;
|
||||
|
||||
float ViewportLength => ScrollLength - reuseCellMarginCount * 2f;
|
||||
|
||||
float PaddingHeadLength => (paddingHead - spacing * 0.5f) / (CellSize + spacing);
|
||||
|
||||
float MaxScrollPosition => ItemsSource.Count
|
||||
- ScrollLength
|
||||
+ reuseCellMarginCount * 2f
|
||||
+ (paddingHead + paddingTail - spacing) / (CellSize + spacing);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Context.ScrollDirection = Scroller.ScrollDirection;
|
||||
Context.CalculateScrollSize = () =>
|
||||
{
|
||||
var interval = CellSize + spacing;
|
||||
var reuseMargin = interval * reuseCellMarginCount;
|
||||
var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f;
|
||||
return (scrollSize, reuseMargin);
|
||||
};
|
||||
|
||||
AdjustCellIntervalAndScrollOffset();
|
||||
Scroller.OnValueChanged(OnScrollerValueChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Scroller"/> のスクロール位置が変更された際の処理.
|
||||
/// </summary>
|
||||
/// <param name="p"><see cref="Scroller"/> のスクロール位置.</param>
|
||||
void OnScrollerValueChanged(float p)
|
||||
{
|
||||
base.UpdatePosition(Scrollable ? ToFancyScrollViewPosition(p) : 0f);
|
||||
|
||||
if (Scroller.Scrollbar)
|
||||
{
|
||||
if (p > ItemsSource.Count - 1)
|
||||
{
|
||||
ShrinkScrollbar(p - (ItemsSource.Count - 1));
|
||||
}
|
||||
else if (p < 0f)
|
||||
{
|
||||
ShrinkScrollbar(-p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// スクロール範囲を超えてスクロールされた量に基づいて, スクロールバーのサイズを縮小します.
|
||||
/// </summary>
|
||||
/// <param name="offset">スクロール範囲を超えてスクロールされた量.</param>
|
||||
void ShrinkScrollbar(float offset)
|
||||
{
|
||||
var scale = 1f - ToFancyScrollViewPosition(offset) / (ViewportLength - PaddingHeadLength);
|
||||
UpdateScrollbarSize((ViewportLength - PaddingHeadLength) * scale);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Refresh()
|
||||
{
|
||||
AdjustCellIntervalAndScrollOffset();
|
||||
RefreshScroller();
|
||||
base.Refresh();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Relayout()
|
||||
{
|
||||
AdjustCellIntervalAndScrollOffset();
|
||||
RefreshScroller();
|
||||
base.Relayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Scroller"/> の各種状態を更新します.
|
||||
/// </summary>
|
||||
protected void RefreshScroller()
|
||||
{
|
||||
Scroller.Draggable = Scrollable;
|
||||
Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength - PaddingHeadLength);
|
||||
Scroller.Position = ToScrollerPosition(currentPosition);
|
||||
|
||||
if (Scroller.Scrollbar)
|
||||
{
|
||||
Scroller.Scrollbar.gameObject.SetActive(Scrollable);
|
||||
UpdateScrollbarSize(ViewportLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UpdateContents(IList<TItemData> items)
|
||||
{
|
||||
Debug.Assert(Context.CalculateScrollSize != null);
|
||||
|
||||
AdjustCellIntervalAndScrollOffset();
|
||||
base.UpdateContents(items);
|
||||
|
||||
Scroller.SetTotalCount(items.Count);
|
||||
RefreshScroller();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// スクロール位置を更新します.
|
||||
/// </summary>
|
||||
/// <param name="position">スクロール位置.</param>
|
||||
protected new void UpdatePosition(float position)
|
||||
{
|
||||
Scroller.Position = ToScrollerPosition(position, 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定したアイテムの位置までジャンプします.
|
||||
/// </summary>
|
||||
/// <param name="itemIndex">アイテムのインデックス.</param>
|
||||
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
|
||||
protected virtual void JumpTo(int itemIndex, float alignment = 0.5f)
|
||||
{
|
||||
Scroller.Position = ToScrollerPosition(itemIndex, alignment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定したアイテムの位置まで移動します.
|
||||
/// </summary>
|
||||
/// <param name="index">アイテムのインデックス.</param>
|
||||
/// <param name="duration">移動にかける秒数.</param>
|
||||
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
|
||||
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
|
||||
protected virtual void ScrollTo(int index, float duration, float alignment = 0.5f, Action onComplete = null)
|
||||
{
|
||||
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定したアイテムの位置まで移動します.
|
||||
/// </summary>
|
||||
/// <param name="index">アイテムのインデックス.</param>
|
||||
/// <param name="duration">移動にかける秒数.</param>
|
||||
/// <param name="easing">移動に使用するイージング.</param>
|
||||
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
|
||||
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
|
||||
protected virtual void ScrollTo(int index, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
|
||||
{
|
||||
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ビューポートとコンテンツの長さに基づいてスクロールバーのサイズを更新します.
|
||||
/// </summary>
|
||||
/// <param name="viewportLength">ビューポートのサイズ.</param>
|
||||
protected void UpdateScrollbarSize(float viewportLength)
|
||||
{
|
||||
var contentLength = Mathf.Max(ItemsSource.Count + (paddingHead + paddingTail - spacing) / (CellSize + spacing), 1);
|
||||
Scroller.Scrollbar.size = Scrollable ? Mathf.Clamp01(viewportLength / contentLength) : 1f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Scroller"/> が扱うスクロール位置を <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置に変換します.
|
||||
/// </summary>
|
||||
/// <param name="position"><see cref="Scroller"/> が扱うスクロール位置.</param>
|
||||
/// <returns><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</returns>
|
||||
protected float ToFancyScrollViewPosition(float position)
|
||||
{
|
||||
return position / Mathf.Max(ItemsSource.Count - 1, 1) * MaxScrollPosition - PaddingHeadLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
|
||||
/// </summary>
|
||||
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
|
||||
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
|
||||
protected float ToScrollerPosition(float position)
|
||||
{
|
||||
return (position + PaddingHeadLength) / MaxScrollPosition * Mathf.Max(ItemsSource.Count - 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置を <see cref="Scroller"/> が扱うスクロール位置に変換します.
|
||||
/// </summary>
|
||||
/// <param name="position"><see cref="FancyScrollRect{TItemData, TContext}"/> が扱うスクロール位置.</param>
|
||||
/// <param name="alignment">ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾).</param>
|
||||
/// <returns><see cref="Scroller"/> が扱うスクロール位置.</returns>
|
||||
protected float ToScrollerPosition(float position, float alignment = 0.5f)
|
||||
{
|
||||
var offset = alignment * (ScrollLength - (1f + reuseCellMarginCount * 2f))
|
||||
+ (1f - alignment - 0.5f) * spacing / (CellSize + spacing);
|
||||
return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定された設定を実現するための
|
||||
/// <see cref="FancyScrollView{TItemData,TContext}.cellInterval"/> と
|
||||
/// <see cref="FancyScrollView{TItemData,TContext}.scrollOffset"/> を計算して適用します.
|
||||
/// </summary>
|
||||
protected void AdjustCellIntervalAndScrollOffset()
|
||||
{
|
||||
var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f);
|
||||
cellInterval = (CellSize + spacing) / totalSize;
|
||||
scrollOffset = cellInterval * (1f + reuseCellMarginCount);
|
||||
}
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
AdjustCellIntervalAndScrollOffset();
|
||||
|
||||
if (loop)
|
||||
{
|
||||
loop = false;
|
||||
Debug.LogError("Loop is currently not supported in FancyScrollRect.");
|
||||
}
|
||||
|
||||
if (Scroller.SnapEnabled)
|
||||
{
|
||||
Scroller.SnapEnabled = false;
|
||||
Debug.LogError("Snap is currently not supported in FancyScrollRect.");
|
||||
}
|
||||
|
||||
if (Scroller.MovementType == MovementType.Unrestricted)
|
||||
{
|
||||
Scroller.MovementType = MovementType.Elastic;
|
||||
Debug.LogError("MovementType.Unrestricted is currently not supported in FancyScrollRect.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
|
||||
/// 無限スクロールおよびスナップには対応していません.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
|
||||
/// <seealso cref="FancyScrollRect{TItemData, TContext}"/>
|
||||
public abstract class FancyScrollRect<TItemData> : FancyScrollRect<TItemData, FancyScrollRectContext> { }
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66c8eb84fdbde4a4a8273b98227a282d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,56 @@
|
||||
/// Credit setchi (https://github.com/setchi)
|
||||
/// Sourced from - https://github.com/setchi/FancyScrollView
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
|
||||
/// <see cref="FancyCell{TItemData, TContext}.Context"/> が不要な場合は
|
||||
/// 代わりに <see cref="FancyScrollRectCell{TItemData}"/> を使用します.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
|
||||
/// <typeparam name="TContext"><see cref="FancyCell{TItemData, TContext}.Context"/> の型.</typeparam>
|
||||
public abstract class FancyScrollRectCell<TItemData, TContext> : FancyCell<TItemData, TContext>
|
||||
where TContext : class, IFancyScrollRectContext, new()
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override void UpdatePosition(float position)
|
||||
{
|
||||
var (scrollSize, reuseMargin) = Context.CalculateScrollSize();
|
||||
|
||||
var normalizedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f);
|
||||
|
||||
var start = 0.5f * scrollSize;
|
||||
var end = -start;
|
||||
|
||||
UpdatePosition(normalizedPosition, Mathf.Lerp(start, end, position));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// このセルの位置を更新します.
|
||||
/// </summary>
|
||||
/// <param name="normalizedPosition">
|
||||
/// ビューポートの範囲で正規化されたスクロール位置.
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}.reuseCellMarginCount"/> の値に基づいて
|
||||
/// <c>0.0</c> ~ <c>1.0</c> の範囲を超えた値が渡されることがあります.
|
||||
/// </param>
|
||||
/// <param name="localPosition">ローカル位置.</param>
|
||||
protected virtual void UpdatePosition(float normalizedPosition, float localPosition)
|
||||
{
|
||||
transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
|
||||
? new Vector2(-localPosition, 0)
|
||||
: new Vector2(0, localPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData}"/> のセルを実装するための抽象基底クラス.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
|
||||
/// <seealso cref="FancyScrollRectCell{TItemData, TContext}"/>
|
||||
public abstract class FancyScrollRectCell<TItemData> : FancyScrollRectCell<TItemData, FancyScrollRectContext>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09f137a55810740eab42e24ef242dcfa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,16 @@
|
||||
/// Credit setchi (https://github.com/setchi)
|
||||
/// Sourced from - https://github.com/setchi/FancyScrollView
|
||||
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキスト基底クラス.
|
||||
/// </summary>
|
||||
public class FancyScrollRectContext : IFancyScrollRectContext
|
||||
{
|
||||
ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; }
|
||||
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91383bd46cee541a7a03e08cfaa47c16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,16 @@
|
||||
/// Credit setchi (https://github.com/setchi)
|
||||
/// Sourced from - https://github.com/setchi/FancyScrollView
|
||||
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.UI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="FancyScrollRect{TItemData, TContext}"/> のコンテキストインターフェース.
|
||||
/// </summary>
|
||||
public interface IFancyScrollRectContext
|
||||
{
|
||||
ScrollDirection ScrollDirection { get; set; }
|
||||
Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e425de6354b6946c7a3b9f2c807b60fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user