diff --git a/Examples b/Examples
index a5914a9..4878f8d 160000
--- a/Examples
+++ b/Examples
@@ -1 +1 @@
-Subproject commit a5914a92198efb1422bd66f84e8cde6cb0842b87
+Subproject commit 4878f8d7d74c72a334b6204dffc9cb117a9463f2
diff --git a/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs b/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs
index c524730..a225142 100644
--- a/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs
+++ b/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs
@@ -5,26 +5,84 @@ using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// スクロールビューを実装するための抽象基底クラス.
+ /// 無限スクロールおよびスナップに対応しています.
+ /// が不要な場合は
+ /// 代わりに を使用します.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
public abstract class FancyScrollView : MonoBehaviour where TContext : class, new()
{
+ ///
+ /// セル同士の間隔.
+ ///
[SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f;
+
+ ///
+ /// スクロール位置の基準.
+ ///
+ ///
+ /// たとえば、 0.5 を指定してスクロール位置が 0 の場合, 中央に最初のセルが配置されます.
+ ///
[SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f;
+
+ ///
+ /// セルを循環して配置させるどうか.
+ ///
+ ///
+ /// true にすると最後のセルの後に最初のセル, 最初のセルの前に最後のセルが並ぶようになります.
+ /// 無限スクロールを実装する場合は true を指定します.
+ ///
[SerializeField] protected bool loop = false;
+
+ ///
+ /// セルの親要素となる Transform.
+ ///
[SerializeField] protected Transform cellContainer = default;
readonly IList> pool =
new List>();
+ ///
+ /// 初期化済みかどうか.
+ ///
+ protected bool initialized;
+
+ ///
+ /// 現在のスクロール位置.
+ ///
protected float currentPosition;
+ ///
+ /// セルの Prefab.
+ ///
protected abstract GameObject CellPrefab { get; }
+
+ ///
+ /// アイテム一覧のデータ.
+ ///
protected IList ItemsSource { get; set; } = new List();
+
+ ///
+ /// のインスタンス.
+ /// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
+ ///
protected TContext Context { get; } = new TContext();
///
- /// Updates the contents.
+ /// 初期化を行います.
///
- /// Items source.
+ ///
+ /// 最初にセルが生成される直前に呼び出されます.
+ ///
+ protected virtual void Initialize() { }
+
+ ///
+ /// 渡されたアイテム一覧に基づいて表示内容を更新します.
+ ///
+ /// アイテム一覧.
protected virtual void UpdateContents(IList itemsSource)
{
ItemsSource = itemsSource;
@@ -32,18 +90,24 @@ namespace UnityEngine.UI.Extensions
}
///
- /// Refreshes the cells.
+ /// セルの表示内容を更新します.
///
protected virtual void Refresh() => UpdatePosition(currentPosition, true);
///
- /// Updates the scroll position.
+ /// スクロール位置を更新します.
///
- /// Position.
+ /// スクロール位置.
protected virtual void UpdatePosition(float position) => UpdatePosition(position, false);
- protected void UpdatePosition(float position, bool forceRefresh)
+ void UpdatePosition(float position, bool forceRefresh)
{
+ if (!initialized)
+ {
+ Initialize();
+ initialized = true;
+ }
+
currentPosition = position;
var p = position - scrollOffset / cellInterval;
@@ -66,7 +130,8 @@ namespace UnityEngine.UI.Extensions
var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count;
for (var i = 0; i < addCount; i++)
{
- var cell = Instantiate(CellPrefab, cellContainer).GetComponent>();
+ var cell = Instantiate(CellPrefab, cellContainer)
+ .GetComponent>();
if (cell == null)
{
throw new MissingComponentException(
@@ -118,7 +183,9 @@ namespace UnityEngine.UI.Extensions
void LateUpdate()
{
- if (cachedLoop != loop || cachedCellInterval != cellInterval || cachedScrollOffset != scrollOffset)
+ if (cachedLoop != loop ||
+ cachedCellInterval != cellInterval ||
+ cachedScrollOffset != scrollOffset)
{
cachedLoop = loop;
cachedCellInterval = cellInterval;
@@ -130,7 +197,16 @@ namespace UnityEngine.UI.Extensions
#endif
}
+ ///
+ /// のコンテキストクラス.
+ ///
public sealed class FancyScrollViewNullContext { }
+ ///
+ /// スクロールビューを実装するための抽象基底クラス.
+ /// 無限スクロールおよびスナップに対応しています.
+ ///
+ ///
+ ///
public abstract class FancyScrollView : FancyScrollView { }
}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs b/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs
index 7628d28..676a9a7 100644
--- a/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs
+++ b/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs
@@ -3,53 +3,64 @@
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// のセルを実装するための抽象基底クラス.
+ /// が不要な場合は
+ /// 代わりに を使用します.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
public abstract class FancyScrollViewCell : MonoBehaviour where TContext : class, new()
{
///
- /// Gets or sets the index of the data.
+ /// このセルで表示しているデータのインデックス.
///
- /// The index of the data.
public int Index { get; set; } = -1;
///
- /// Gets a value indicating whether this is visible.
+ /// このセルの可視状態.
///
- /// true if is visible; otherwise, false.
public virtual bool IsVisible => gameObject.activeSelf;
///
- /// Gets the context.
+ /// の参照.
+ /// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
///
- /// The context.
protected TContext Context { get; private set; }
///
- /// Setup the context.
+ /// のセットアップを行います.
///
- /// Context.
+ /// コンテキスト.
public virtual void SetupContext(TContext context) => Context = context;
///
- /// Sets the visible.
+ /// このセルの可視状態を設定します.
///
- /// If set to true visible.
+ /// 可視状態なら true, 非可視状態なら false.
public virtual void SetVisible(bool visible) => gameObject.SetActive(visible);
///
- /// Updates the content.
+ /// アイテムデータに基づいてこのセルの表示内容を更新します.
///
- /// Item data.
+ /// アイテムデータ.
public abstract void UpdateContent(TItemData itemData);
///
- /// Updates the position.
+ /// 0.0f ~ 1.0f の値に基づいてこのセルのスクロール位置を更新します.
///
- /// Position.
+ /// ビューポート範囲の正規化されたスクロール位置.
public abstract void UpdatePosition(float position);
}
+ ///
+ /// のセルを実装するための抽象基底クラス.
+ ///
+ /// アイテムのデータ型.
+ ///
public abstract class FancyScrollViewCell : FancyScrollViewCell
{
+ ///
public sealed override void SetupContext(FancyScrollViewNullContext context) => base.SetupContext(context);
}
}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/GridView.meta b/Scripts/Layout/FancyScrollView/GridView.meta
new file mode 100644
index 0000000..f61d146
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c7768f2982b0142ab876d2bb4b597646
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs b/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs
new file mode 100644
index 0000000..e74f517
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs
@@ -0,0 +1,144 @@
+/// Credit setchi (https://github.com/setchi)
+/// Sourced from - https://github.com/setchi/FancyScrollView
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine.UI.Extensions.EasingCore;
+
+namespace UnityEngine.UI.Extensions
+{
+ ///
+ /// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
+ /// 無限スクロールおよびスナップには対応していません.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
+ public abstract class FancyGridView : FancyScrollRect
+ where TContext : class, IFancyScrollRectContext, IFancyGridViewContext, new()
+ {
+ ///
+ /// カラム同士の余白.
+ ///
+ [SerializeField] protected float columnSpacing = 0f;
+
+ GameObject cachedRowPrefab;
+
+ ///
+ /// 行の Prefab.
+ ///
+ ///
+ /// では,
+ /// を行オブジェクトとして使用します.
+ ///
+ protected sealed override GameObject CellPrefab => cachedRowPrefab ?? (cachedRowPrefab = SetupRowTemplate());
+
+ ///
+ /// 一行あたりの要素数.
+ ///
+ protected abstract int ColumnCount { get; }
+
+ ///
+ /// セルのテンプレート.
+ ///
+ protected abstract FancyScrollViewCell CellTemplate { get; }
+
+ ///
+ /// 行オブジェクトのテンプレート.
+ ///
+ protected abstract FancyGridViewRow RowTemplate { get; }
+
+ ///
+ /// アイテムの総数.
+ ///
+ public int DataCount { get; private set; }
+
+ ///
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ Debug.Assert(RowTemplate != null);
+ Debug.Assert(CellTemplate != null);
+ Debug.Assert(ColumnCount > 0);
+
+ Context.CellTemplate = CellTemplate.gameObject;
+ Context.ScrollDirection = Scroller.ScrollDirection;
+ Context.GetColumnCount = () => ColumnCount;
+ Context.GetColumnSpacing = () => columnSpacing;
+ }
+
+ ///
+ /// 行オブジェクトのセットアップを行います.
+ ///
+ /// 行を構成する GameObject.
+ protected virtual GameObject SetupRowTemplate()
+ {
+ var cell = CellTemplate.GetComponent();
+ var row = RowTemplate.GetComponent();
+
+ row.sizeDelta = Scroller.ScrollDirection == ScrollDirection.Horizontal
+ ? new Vector2(cell.rect.width, row.sizeDelta.y)
+ : new Vector2(row.sizeDelta.x, cell.rect.height);
+
+ return row.gameObject;
+ }
+
+ ///
+ /// 渡されたアイテム一覧に基づいて表示内容を更新します.
+ ///
+ /// アイテム一覧.
+ public virtual void UpdateContents(IList items)
+ {
+ DataCount = items.Count;
+
+ var rows = items
+ .Select((item, index) => (item, index))
+ .GroupBy(
+ x => x.index / ColumnCount,
+ x => x.item)
+ .Select(group => group.ToArray())
+ .ToArray();
+
+ UpdateContents(rows);
+ }
+
+ ///
+ /// 指定したアイテムの位置まで移動します.
+ ///
+ /// アイテムのインデックス.
+ /// 移動にかける秒数.
+ /// .
+ /// 移動が完了した際に呼び出されるコールバック.
+ public override void ScrollTo(int itemIndex, float duration, Alignment alignment = Alignment.Center, Action onComplete = null)
+ {
+ var rowIndex = itemIndex / Context.GetColumnCount();
+ base.ScrollTo(rowIndex, duration, alignment, onComplete);
+ }
+
+ ///
+ /// 指定したアイテムの位置まで移動します.
+ ///
+ /// アイテムのインデックス.
+ /// 移動にかける秒数.
+ /// 移動に使用するイージング.
+ /// .
+ /// 移動が完了した際に呼び出されるコールバック.
+ public override void ScrollTo(int itemIndex, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null)
+ {
+ var rowIndex = itemIndex / Context.GetColumnCount();
+ base.ScrollTo(rowIndex, duration, easing, alignment, onComplete);
+ }
+
+ ///
+ /// 指定したアイテムの位置までジャンプします.
+ ///
+ /// アイテムのインデックス.
+ /// .
+ public virtual void JumpTo(int itemIndex, Alignment alignment = Alignment.Center)
+ {
+ var rowIndex = itemIndex / Context.GetColumnCount();
+ UpdatePosition(rowIndex, alignment);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs.meta b/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs.meta
new file mode 100644
index 0000000..e1254c5
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e806c8d18b1ff45bb87e9a5b87ec85e3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs
new file mode 100644
index 0000000..05090c8
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs
@@ -0,0 +1,23 @@
+/// Credit setchi (https://github.com/setchi)
+/// Sourced from - https://github.com/setchi/FancyScrollView
+
+using System;
+
+namespace UnityEngine.UI.Extensions
+{
+ ///
+ /// のコンテキスト基底クラス.
+ ///
+ public class FancyGridViewContext : IFancyGridViewContext, IFancyScrollRectContext
+ {
+ Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
+
+ GameObject IFancyGridViewContext.CellTemplate { get; set; }
+
+ ScrollDirection IFancyGridViewContext.ScrollDirection { get; set; }
+
+ public Func GetColumnCount { get; set; }
+
+ public Func GetColumnSpacing { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs.meta b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs.meta
new file mode 100644
index 0000000..49a8040
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 954a17398bfb54ee7baac3d7ab7e822c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs
new file mode 100644
index 0000000..3613bce
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs
@@ -0,0 +1,81 @@
+/// Credit setchi (https://github.com/setchi)
+/// Sourced from - https://github.com/setchi/FancyScrollView
+
+using System.Linq;
+
+namespace UnityEngine.UI.Extensions
+{
+ ///
+ /// の行を実装するための抽象基底クラス.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
+ public abstract class FancyGridViewRow : FancyScrollRectCell
+ where TContext : class, IFancyScrollRectContext, IFancyGridViewContext, new()
+ {
+ ///
+ /// この行で表示するセルの配列.
+ ///
+ protected virtual FancyScrollViewCell[] Cells { get; private set; }
+
+ ///
+ /// この行で表示するセルの配列をインスタンス化します.
+ ///
+ /// この行で表示するセルの配列.
+ protected virtual FancyScrollViewCell[] InstantiateCells()
+ {
+ return Enumerable.Range(0, Context.GetColumnCount())
+ .Select(_ => Instantiate(Context.CellTemplate, transform))
+ .Select(x => x.GetComponent>())
+ .ToArray();
+ }
+
+ ///
+ public override void SetupContext(TContext context)
+ {
+ base.SetupContext(context);
+
+ Cells = InstantiateCells();
+ Debug.Assert(Cells.Length == Context.GetColumnCount());
+
+ for (var i = 0; i < Cells.Length; i++)
+ {
+ Cells[i].SetupContext(context);
+ }
+ }
+
+ ///
+ public override void UpdateContent(TItemData[] rowContents)
+ {
+ for (var i = 0; i < Cells.Length; i++)
+ {
+ Cells[i].Index = i + Index * Context.GetColumnCount();
+ Cells[i].SetVisible(i < rowContents.Length);
+
+ if (Cells[i].IsVisible)
+ {
+ Cells[i].UpdateContent(rowContents[i]);
+ }
+ }
+ }
+
+ ///
+ public override void UpdatePosition(float position)
+ {
+ base.UpdatePosition(position);
+
+ for (var i = 0; i < Cells.Length; i++)
+ {
+ Cells[i].UpdatePosition(position);
+ }
+ }
+
+ ///
+ protected override void UpdatePosition(float position, float viewportPosition)
+ {
+ transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal
+ ? new Vector2(viewportPosition, transform.localPosition.y)
+ : new Vector2(transform.localPosition.x, viewportPosition);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta
new file mode 100644
index 0000000..dbcb384
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 84300901ad8704c11b39587ed6d87468
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs b/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs
new file mode 100644
index 0000000..70221da
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs
@@ -0,0 +1,18 @@
+/// Credit setchi (https://github.com/setchi)
+/// Sourced from - https://github.com/setchi/FancyScrollView
+
+using System;
+
+namespace UnityEngine.UI.Extensions
+{
+ ///
+ /// のコンテキストインターフェース.
+ ///
+ public interface IFancyGridViewContext
+ {
+ GameObject CellTemplate { get; set; }
+ ScrollDirection ScrollDirection { get; set; }
+ Func GetColumnCount { get; set; }
+ Func GetColumnSpacing { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs.meta b/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs.meta
new file mode 100644
index 0000000..1bed129
--- /dev/null
+++ b/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3cf2d53d9c81945c28f7c558a7c40ba3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs
index b6b76d3..54b5a66 100644
--- a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs
+++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs
@@ -7,38 +7,86 @@ using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
+ /// 無限スクロールおよびスナップには対応していません.
+ /// が不要な場合は
+ /// 代わりに を使用します.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
[RequireComponent(typeof(Scroller))]
public abstract class FancyScrollRect : FancyScrollView
where TContext : class, IFancyScrollRectContext, new()
{
+ ///
+ /// スクロール中にセルが再利用されるまでの余白のセル数.
+ ///
+ ///
+ /// 0 を指定するとセルが完全に隠れた直後に再利用されます.
+ /// 1 以上を指定すると, そのセル数だけ余分にスクロールしてから再利用されます.
+ ///
[SerializeField] protected float reuseCellMarginCount = 0f;
+
+ ///
+ /// コンテンツ先頭の余白.
+ ///
[SerializeField] protected float paddingHead = 0f;
+
+ ///
+ /// コンテンツ末尾の余白.
+ ///
[SerializeField] protected float paddingTail = 0f;
+
+ ///
+ /// セル同士の余白.
+ ///
[SerializeField] protected float spacing = 0f;
- protected virtual float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f;
-
- protected virtual float ViewportLength => ScrollLength - reuseCellMarginCount * 2f;
-
- protected virtual float MaxScrollPosition => ItemsSource.Count
- - ScrollLength
- + reuseCellMarginCount * 2f
- + (paddingHead + (paddingTail - spacing)) / (CellSize + spacing);
-
- protected virtual bool ScrollEnabled => MaxScrollPosition > 0f;
+ ///
+ /// スクロール可能かどうか.
+ ///
+ ///
+ /// アイテム数が十分少なくビューポート内に全てのセルが収まっている場合は false, それ以外は true になります.
+ ///
+ protected virtual bool Scrollable => MaxScrollPosition > 0f;
+ ///
+ /// セルのサイズ.
+ ///
protected virtual float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal
? CellRectTransform.rect.width
: CellRectTransform.rect.height;
Scroller cachedScroller;
+
+ ///
+ /// スクロール位置を制御する のインスタンス.
+ ///
+ ///
+ /// のスクロール位置を変更する際は必ず を使用して変換した位置を使用してください.
+ ///
protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent());
RectTransform cachedCellRect;
RectTransform CellRectTransform => cachedCellRect ?? (cachedCellRect = CellPrefab.transform as RectTransform);
- protected virtual void Awake()
+ 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);
+
+ ///
+ protected override void Initialize()
{
+ base.Initialize();
+
Context.CalculateScrollSize = () =>
{
var interval = CellSize + spacing;
@@ -46,17 +94,18 @@ namespace UnityEngine.UI.Extensions
var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f;
return (scrollSize, reuseMargin);
};
- }
- protected virtual void Start()
- {
AdjustCellIntervalAndScrollOffset();
Scroller.OnValueChanged(OnScrollerValueChanged);
}
+ ///
+ /// のスクロール位置が変更された際の処理.
+ ///
+ /// のスクロール位置.
void OnScrollerValueChanged(float p)
{
- base.UpdatePosition(ScrollEnabled ? ToFancyScrollViewPosition(p) : 0f);
+ base.UpdatePosition(Scrollable ? ToFancyScrollViewPosition(p) : 0f);
if (Scroller.Scrollbar)
{
@@ -71,12 +120,41 @@ namespace UnityEngine.UI.Extensions
}
}
+ ///
+ /// スクロール範囲を超えてスクロールされた量に基づいて, スクロールバーのサイズを縮小します.
+ ///
+ /// スクロール範囲を超えてスクロールされた量.
void ShrinkScrollbar(float offset)
{
- var scale = 1f - ToFancyScrollViewPosition(offset) / ViewportLength;
- UpdateScrollbarSize(ViewportLength * scale / Mathf.Max(ItemsSource.Count, 1));
+ var scale = 1f - ToFancyScrollViewPosition(offset) / (ViewportLength - PaddingHeadLength);
+ UpdateScrollbarSize((ViewportLength - PaddingHeadLength) * scale);
}
+ ///
+ protected override void Refresh()
+ {
+ AdjustCellIntervalAndScrollOffset();
+ RefreshScroller();
+ base.Refresh();
+ }
+
+ ///
+ /// の各種状態を更新します.
+ ///
+ 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);
+ }
+ }
+
+ ///
protected override void UpdateContents(IList items)
{
Debug.Assert(Context.CalculateScrollSize != null);
@@ -85,64 +163,96 @@ namespace UnityEngine.UI.Extensions
base.UpdateContents(items);
Scroller.SetTotalCount(items.Count);
- Scroller.Draggable = ScrollEnabled;
- Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength);
- Scroller.Position = ToScrollerPosition(currentPosition);
-
- if (Scroller.Scrollbar)
- {
- Scroller.Scrollbar.gameObject.SetActive(ScrollEnabled);
- UpdateScrollbarSize(ViewportLength / Mathf.Max(ItemsSource.Count, 1));
- }
+ RefreshScroller();
}
+ ///
+ /// スクロール位置を更新します.
+ ///
+ /// スクロール位置.
protected new void UpdatePosition(float position)
{
UpdatePosition(position, Alignment.Center);
}
+ ///
+ /// スクロール位置を更新します.
+ ///
+ /// スクロール位置.
+ /// .
protected virtual void UpdatePosition(float position, Alignment alignment)
{
Scroller.Position = ToScrollerPosition(position, alignment);
}
+ ///
+ /// 指定したアイテムの位置まで移動します.
+ ///
+ /// アイテムのインデックス.
+ /// 移動にかける秒数.
+ /// .
+ /// 移動が完了した際に呼び出されるコールバック.
public virtual void ScrollTo(int index, float duration, Alignment alignment = Alignment.Center, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete);
}
+ ///
+ /// 指定したアイテムの位置まで移動します.
+ ///
+ /// アイテムのインデックス.
+ /// 移動にかける秒数.
+ /// 移動に使用するイージング.
+ /// .
+ /// 移動が完了した際に呼び出されるコールバック.
public virtual void ScrollTo(int index, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null)
{
Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete);
}
- protected void UpdateScrollbarSize(float size)
+ ///
+ /// ビューポートとコンテンツの長さに基づいてスクロールバーのサイズを更新します.
+ ///
+ /// ビューポートのサイズ.
+ protected void UpdateScrollbarSize(float viewportLength)
{
- Scroller.Scrollbar.size = ScrollEnabled ? Mathf.Clamp01(size) : 1f;
+ var contentLength = Mathf.Max(ItemsSource.Count + (paddingHead + paddingTail - spacing) / (CellSize + spacing), 1);
+ Scroller.Scrollbar.size = Scrollable ? Mathf.Clamp01(viewportLength / contentLength) : 1f;
}
- protected virtual float ToFancyScrollViewPosition(float position)
+ ///
+ /// が扱うスクロール位置を が扱うスクロール位置に変換します.
+ ///
+ /// が扱うスクロール位置.
+ /// が扱うスクロール位置.
+ protected float ToFancyScrollViewPosition(float position)
{
- return position
- / Mathf.Max(ItemsSource.Count - 1, 1)
- * MaxScrollPosition
- - (paddingHead - spacing * 0.5f) / (CellSize + spacing);
+ return position / Mathf.Max(ItemsSource.Count - 1, 1) * MaxScrollPosition - PaddingHeadLength;
}
- protected virtual float ToScrollerPosition(float position)
+ ///
+ /// が扱うスクロール位置を が扱うスクロール位置に変換します.
+ ///
+ /// が扱うスクロール位置.
+ /// が扱うスクロール位置.
+ protected float ToScrollerPosition(float position)
{
- return (position + (paddingHead - spacing * 0.5f) / (CellSize + spacing))
- / MaxScrollPosition
- * Mathf.Max(ItemsSource.Count - 1, 1);
+ return (position + PaddingHeadLength) / MaxScrollPosition * Mathf.Max(ItemsSource.Count - 1, 1);
}
- protected virtual float ToScrollerPosition(float position, Alignment alignment = Alignment.Center)
+ ///
+ /// が扱うスクロール位置を が扱うスクロール位置に変換します.
+ ///
+ /// が扱うスクロール位置.
+ /// .
+ /// が扱うスクロール位置.
+ protected float ToScrollerPosition(float position, Alignment alignment = Alignment.Center)
{
var offset = (ScrollLength - (1f + reuseCellMarginCount * 2f)) * GetAnchore(alignment);
return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition));
}
- protected virtual float GetAnchore(Alignment alignment)
+ float GetAnchore(Alignment alignment)
{
switch (alignment)
{
@@ -153,7 +263,12 @@ namespace UnityEngine.UI.Extensions
}
}
- void AdjustCellIntervalAndScrollOffset()
+ ///
+ /// 指定された設定を実現するための
+ /// と
+ /// を計算して適用します.
+ ///
+ protected void AdjustCellIntervalAndScrollOffset()
{
var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f);
cellInterval = (CellSize + spacing) / totalSize;
@@ -187,5 +302,11 @@ namespace UnityEngine.UI.Extensions
}
}
+ ///
+ /// ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス.
+ /// 無限スクロールおよびスナップには対応していません.
+ ///
+ /// アイテムのデータ型.
+ ///
public abstract class FancyScrollRect : FancyScrollRect { }
-}
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs
index 6136b81..4e368c9 100644
--- a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs
+++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs
@@ -3,9 +3,17 @@
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// のセルを実装するための抽象基底クラス.
+ /// が不要な場合は
+ /// 代わりに を使用します.
+ ///
+ /// アイテムのデータ型.
+ /// の型.
public abstract class FancyScrollRectCell : FancyScrollViewCell
where TContext : class, IFancyScrollRectContext, new()
{
+ ///
public override void UpdatePosition(float position)
{
var (scrollSize, reuseMargin) = Context.CalculateScrollSize();
@@ -18,11 +26,26 @@ namespace UnityEngine.UI.Extensions
UpdatePosition(unclampedPosition, Mathf.Lerp(start, end, position));
}
+ ///
+ /// このセルの位置を更新します.
+ ///
+ ///
+ /// ビューポートの範囲で正規化されたスクロール位置.
+ /// の値に基づいて
+ /// 0.0 ~ 1.0 の範囲を超えた値が渡されることがあります.
+ ///
+ /// ローカル位置.
protected virtual void UpdatePosition(float position, float viewportPosition) { }
}
+ ///
+ /// のセルを実装するための抽象基底クラス.
+ ///
+ /// アイテムのデータ型.
+ ///
public abstract class FancyScrollRectCell : FancyScrollRectCell
{
+ ///
public sealed override void SetupContext(FancyScrollRectContext context) => base.SetupContext(context);
}
-}
+}
\ No newline at end of file
diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs
index 2e5ea39..17c113f 100644
--- a/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs
+++ b/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs
@@ -5,6 +5,9 @@ using System;
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// のコンテキスト基底クラス.
+ ///
public class FancyScrollRectContext : IFancyScrollRectContext
{
Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; }
diff --git a/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs
index 8ba33dd..0a83479 100644
--- a/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs
+++ b/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs
@@ -5,7 +5,9 @@ using System;
namespace UnityEngine.UI.Extensions
{
-
+ ///
+ /// のコンテキストインターフェース.
+ ///
public interface IFancyScrollRectContext
{
Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; }
diff --git a/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs
index 31a2801..262cabc 100644
--- a/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs
+++ b/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs
@@ -7,15 +7,82 @@ using UnityEngine.UI.Extensions.EasingCore;
namespace UnityEngine.UI.Extensions
{
+ ///
+ /// スクロール位置の制御を行うコンポーネント.
+ ///
public class Scroller : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
[SerializeField] RectTransform viewport = default;
+
+ ///
+ /// ビューポートのサイズ.
+ ///
+ public float ViewportSize => scrollDirection == ScrollDirection.Horizontal
+ ? viewport.rect.size.x
+ : viewport.rect.size.y;
+
[SerializeField] ScrollDirection scrollDirection = ScrollDirection.Vertical;
+
+ ///
+ /// スクロール方向.
+ ///
+ public ScrollDirection ScrollDirection => scrollDirection;
+
[SerializeField] MovementType movementType = MovementType.Elastic;
+
+ ///
+ /// コンテンツがスクロール範囲を越えて移動するときに使用する挙動.
+ ///
+ public MovementType MovementType
+ {
+ get => movementType;
+ set => movementType = value;
+ }
+
[SerializeField] float elasticity = 0.1f;
+
+ ///
+ /// コンテンツがスクロール範囲を越えて移動するときに使用する弾力性の量.
+ ///
+ public float Elasticity
+ {
+ get => elasticity;
+ set => elasticity = value;
+ }
+
[SerializeField] float scrollSensitivity = 1f;
+
+ ///
+ /// の端から端まで Drag したときのスクロール位置の変化量.
+ ///
+ public float ScrollSensitivity
+ {
+ get => scrollSensitivity;
+ set => scrollSensitivity = value;
+ }
+
[SerializeField] bool inertia = true;
+
+ ///
+ /// 慣性を使用するかどうか. true を指定すると慣性が有効に, false を指定すると慣性が無効になります.
+ ///
+ public bool Inertia
+ {
+ get => inertia;
+ set => inertia = value;
+ }
+
[SerializeField] float decelerationRate = 0.03f;
+
+ ///
+ /// スクロールの減速率. が true の場合のみ有効です.
+ ///
+ public float DecelerationRate
+ {
+ get => decelerationRate;
+ set => decelerationRate = value;
+ }
+
[SerializeField]
Snap snap = new Snap
{
@@ -24,41 +91,41 @@ namespace UnityEngine.UI.Extensions
Duration = 0.3f,
Easing = Ease.InOutCubic
};
- [SerializeField] bool draggable = true;
- [SerializeField] Scrollbar scrollbar = default;
-
- public ScrollDirection ScrollDirection => scrollDirection;
-
- public MovementType MovementType
- {
- get => movementType;
- set => movementType = value;
- }
-
- public float ViewportSize => scrollDirection == ScrollDirection.Horizontal
- ? viewport.rect.size.x
- : viewport.rect.size.y;
+ ///
+ /// true ならスナップし, falseならスナップしません.
+ ///
+ ///
+ /// スナップを有効にすると, 慣性でスクロールが止まる直前に最寄りのセルへ移動します.
+ ///
public bool SnapEnabled
{
get => snap.Enable;
set => snap.Enable = value;
}
- public float ScrollSensitivity
- {
- get => scrollSensitivity;
- set => scrollSensitivity = value;
- }
+ [SerializeField] bool draggable = true;
+ ///
+ /// Drag 入力を受付けるかどうか.
+ ///
public bool Draggable
{
get => draggable;
set => draggable = value;
}
+ [SerializeField] Scrollbar scrollbar = default;
+
+ ///
+ /// スクロールバーのオブジェクト.
+ ///
public Scrollbar Scrollbar => scrollbar;
+ ///
+ /// 現在のスクロール位置.
+ ///
+ ///
public float Position
{
get => currentPosition;
@@ -137,16 +204,51 @@ namespace UnityEngine.UI.Extensions
}
}
+ ///
+ /// スクロール位置が変化したときのコールバックを設定します.
+ ///
+ /// スクロール位置が変化したときのコールバック.
public void OnValueChanged(Action callback) => onValueChanged = callback;
+ ///
+ /// 選択位置が変化したときのコールバックを設定します.
+ ///
+ /// 選択位置が変化したときのコールバック.
public void OnSelectionChanged(Action callback) => onSelectionChanged = callback;
+ ///
+ /// アイテムの総数を設定します.
+ ///
+ ///
+ /// を元に最大スクロール位置を計算します.
+ ///
+ /// アイテムの総数.
public void SetTotalCount(int totalCount) => this.totalCount = totalCount;
+ ///
+ /// 指定した位置まで移動します.
+ ///
+ /// スクロール位置. 0f ~ totalCount - 1f の範囲.
+ /// 移動にかける秒数.
+ /// 移動が完了した際に呼び出されるコールバック.
public void ScrollTo(float position, float duration, Action onComplete = null) => ScrollTo(position, duration, Ease.OutCubic, onComplete);
+ ///
+ /// 指定した位置まで移動します.
+ ///
+ /// スクロール位置. 0f ~ totalCount - 1f の範囲.
+ /// 移動にかける秒数.
+ /// 移動に使用するイージング.
+ /// 移動が完了した際に呼び出されるコールバック.
public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, EasingFunction.Get(easing), onComplete);
+ ///
+ /// 指定した位置まで移動します.
+ ///
+ /// スクロール位置. 0f ~ totalCount - 1f の範囲.
+ /// 移動にかける秒数.
+ /// 移動に使用するイージング関数.
+ /// 移動が完了した際に呼び出されるコールバック.
public void ScrollTo(float position, float duration, Func easingFunction, Action onComplete = null)
{
if (duration <= 0f)
@@ -170,6 +272,10 @@ namespace UnityEngine.UI.Extensions
UpdateSelection(Mathf.RoundToInt(CircularPosition(autoScrollState.EndPosition, totalCount)));
}
+ ///
+ /// 指定したインデックスの位置までジャンプします.
+ ///
+ /// アイテムのインデックス.
public void JumpTo(int index)
{
if (index < 0 || index > totalCount - 1)
@@ -186,6 +292,13 @@ namespace UnityEngine.UI.Extensions
UpdatePosition(index);
}
+ ///
+ /// から に移動する際の移動方向を返します.
+ /// スクロール範囲が無制限に設定されている場合は, 最短距離の移動方向を返します.
+ ///
+ /// 移動元のインデックス.
+ /// 移動先のインデックス.
+ ///
public MovementDirection GetMovementDirection(int sourceIndex, int destIndex)
{
var movementAmount = CalculateMovementAmount(sourceIndex, destIndex);
@@ -198,6 +311,7 @@ namespace UnityEngine.UI.Extensions
: MovementDirection.Down;
}
+ ///
void IBeginDragHandler.OnBeginDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
@@ -216,6 +330,7 @@ namespace UnityEngine.UI.Extensions
autoScrollState.Reset();
}
+ ///
void IDragHandler.OnDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left || !dragging)
@@ -252,6 +367,7 @@ namespace UnityEngine.UI.Extensions
UpdatePosition(position);
}
+ ///
void IEndDragHandler.OnEndDrag(PointerEventData eventData)
{
if (!draggable || eventData.button != PointerEventData.InputButton.Left)
@@ -400,14 +516,14 @@ namespace UnityEngine.UI.Extensions
return Mathf.Clamp(destPosition, 0, totalCount - 1) - sourcePosition;
}
- var movementAmount = CircularPosition(destPosition, totalCount) - CircularPosition(sourcePosition, totalCount);
+ var amount = CircularPosition(destPosition, totalCount) - CircularPosition(sourcePosition, totalCount);
- if (Mathf.Abs(movementAmount) > totalCount * 0.5f)
+ if (Mathf.Abs(amount) > totalCount * 0.5f)
{
- movementAmount = Mathf.Sign(-movementAmount) * (totalCount - Mathf.Abs(movementAmount));
+ amount = Mathf.Sign(-amount) * (totalCount - Mathf.Abs(amount));
}
- return movementAmount;
+ return amount;
}
float CircularPosition(float p, int size) => size < 1 ? 0 : p < 0 ? size - 1 + (p + 1) % size : p % size;