diff --git a/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs b/Runtime/Scripts/Layout/FancyScrollView/Core/FancyCell.cs similarity index 75% rename from Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs rename to Runtime/Scripts/Layout/FancyScrollView/Core/FancyCell.cs index 676a9a7..17928d1 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/Core/FancyCell.cs @@ -1,16 +1,17 @@ /// Credit setchi (https://github.com/setchi) /// Sourced from - https://github.com/setchi/FancyScrollView + namespace UnityEngine.UI.Extensions { /// /// のセルを実装するための抽象基底クラス. - /// が不要な場合は - /// 代わりに を使用します. + /// が不要な場合は + /// 代わりに を使用します. /// /// アイテムのデータ型. /// の型. - public abstract class FancyScrollViewCell : MonoBehaviour where TContext : class, new() + public abstract class FancyCell : MonoBehaviour where TContext : class, new() { /// /// このセルで表示しているデータのインデックス. @@ -29,10 +30,15 @@ namespace UnityEngine.UI.Extensions protected TContext Context { get; private set; } /// - /// のセットアップを行います. + /// をセットします. /// /// コンテキスト. - public virtual void SetupContext(TContext context) => Context = context; + public virtual void SetContext(TContext context) => Context = context; + + /// + /// 初期化を行います. + /// + public virtual void Initialize() { } /// /// このセルの可視状態を設定します. @@ -57,10 +63,10 @@ namespace UnityEngine.UI.Extensions /// のセルを実装するための抽象基底クラス. /// /// アイテムのデータ型. - /// - public abstract class FancyScrollViewCell : FancyScrollViewCell + /// + public abstract class FancyCell : FancyCell { /// - public sealed override void SetupContext(FancyScrollViewNullContext context) => base.SetupContext(context); + public sealed override void SetContext(NullContext context) => base.SetContext(context); } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs.meta b/Runtime/Scripts/Layout/FancyScrollView/Core/FancyCell.cs.meta similarity index 100% rename from Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs.meta rename to Runtime/Scripts/Layout/FancyScrollView/Core/FancyCell.cs.meta diff --git a/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs b/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs index a225142..df3f942 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs @@ -42,8 +42,7 @@ namespace UnityEngine.UI.Extensions /// [SerializeField] protected Transform cellContainer = default; - readonly IList> pool = - new List>(); + readonly IList> pool = new List>(); /// /// 初期化済みかどうか. @@ -90,7 +89,12 @@ namespace UnityEngine.UI.Extensions } /// - /// セルの表示内容を更新します. + /// セルのレイアウトを強制的に更新します. + /// + protected virtual void Relayout() => UpdatePosition(currentPosition, false); + + /// + /// セルのレイアウトと表示内容を強制的に更新します. /// protected virtual void Refresh() => UpdatePosition(currentPosition, true); @@ -130,16 +134,16 @@ 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( - $"FancyScrollViewCell<{typeof(TItemData).FullName}, {typeof(TContext).FullName}> " + - $"component not found in {CellPrefab.name}."); + throw new MissingComponentException(string.Format( + "FancyCell<{0}, {1}> component not found in {2}.", + typeof(TItemData).FullName, typeof(TContext).FullName, CellPrefab.name)); } - cell.SetupContext(Context); + cell.SetContext(Context); + cell.Initialize(); cell.SetVisible(false); pool.Add(cell); } @@ -200,7 +204,7 @@ namespace UnityEngine.UI.Extensions /// /// のコンテキストクラス. /// - public sealed class FancyScrollViewNullContext { } + public sealed class NullContext { } /// /// スクロールビューを実装するための抽象基底クラス. @@ -208,5 +212,5 @@ namespace UnityEngine.UI.Extensions /// /// /// - public abstract class FancyScrollView : FancyScrollView { } + public abstract class FancyScrollView : FancyScrollView { } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs new file mode 100644 index 0000000..5aa3698 --- /dev/null +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs @@ -0,0 +1,72 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +using System.Linq; + +namespace UnityEngine.UI.Extensions +{ + /// + /// 複数の を持つセルグループ実装するための抽象基底クラス. + /// + /// アイテムのデータ型. + /// の型. + public abstract class FancyCellGroup : FancyCell + where TContext : class, IFancyCellGroupContext, new() + { + /// + /// このグループで表示するセルの配列. + /// + protected virtual FancyCell[] Cells { get; private set; } + + /// + /// このグループで表示するセルの配列をインスタンス化します. + /// + /// このグループで表示するセルの配列. + protected virtual FancyCell[] InstantiateCells() + { + return Enumerable.Range(0, Context.GetGroupCount()) + .Select(_ => Instantiate(Context.CellTemplate, transform)) + .Select(x => x.GetComponent>()) + .ToArray(); + } + + /// + public override void Initialize() + { + Cells = InstantiateCells(); + Debug.Assert(Cells.Length == Context.GetGroupCount()); + + for (var i = 0; i < Cells.Length; i++) + { + Cells[i].SetContext(Context); + Cells[i].Initialize(); + } + } + + /// + public override void UpdateContent(TItemData[] contents) + { + var firstCellIndex = Index * Context.GetGroupCount(); + + for (var i = 0; i < Cells.Length; i++) + { + Cells[i].Index = i + firstCellIndex; + Cells[i].SetVisible(i < contents.Length); + + if (Cells[i].IsVisible) + { + Cells[i].UpdateContent(contents[i]); + } + } + } + + /// + public override void UpdatePosition(float position) + { + for (var i = 0; i < Cells.Length; i++) + { + Cells[i].UpdatePosition(position); + } + } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs.meta similarity index 83% rename from Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta rename to Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs.meta index 9c0ac48..65a825d 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs.meta +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyCellGroup.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3e786463ba934403cacf7e8c0d5822d7 +guid: 5d97e25c7748b8d44acd2298e509c8f1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs index e74f517..4e1417b 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridView.cs @@ -11,77 +11,106 @@ namespace UnityEngine.UI.Extensions /// /// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス. /// 無限スクロールおよびスナップには対応していません. + /// が不要な場合は + /// 代わりに を使用します. /// /// アイテムのデータ型. /// の型. public abstract class FancyGridView : FancyScrollRect - where TContext : class, IFancyScrollRectContext, IFancyGridViewContext, new() + where TContext : class, IFancyGridViewContext, new() { /// - /// カラム同士の余白. + /// デフォルトのセルグループクラス. /// - [SerializeField] protected float columnSpacing = 0f; - - GameObject cachedRowPrefab; + protected abstract class DefaultCellGroup : FancyCellGroup { } /// - /// 行の Prefab. + /// 最初にセルを配置する軸方向のセル同士の余白. + /// + [SerializeField] protected float startAxisSpacing = 0f; + + /// + /// 最初にセルを配置する軸方向のセル数. + /// + [SerializeField] protected int startAxisCellCount = 4; + + /// + /// セルのサイズ. + /// + [SerializeField] protected Vector2 cellSize = new Vector2(100f, 100f); + + /// + /// セルのグループ Prefab. /// /// /// では, - /// を行オブジェクトとして使用します. + /// を最初にセルを配置する軸方向のセルコンテナとして使用します. /// - protected sealed override GameObject CellPrefab => cachedRowPrefab ?? (cachedRowPrefab = SetupRowTemplate()); + protected sealed override GameObject CellPrefab => cellGroupTemplate; - /// - /// 一行あたりの要素数. - /// - protected abstract int ColumnCount { get; } - - /// - /// セルのテンプレート. - /// - protected abstract FancyScrollViewCell CellTemplate { get; } - - /// - /// 行オブジェクトのテンプレート. - /// - protected abstract FancyGridViewRow RowTemplate { get; } + /// + protected override float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal + ? cellSize.x + : cellSize.y; /// /// アイテムの総数. /// public int DataCount { get; private set; } + GameObject cellGroupTemplate; + /// protected override void Initialize() { base.Initialize(); - Debug.Assert(RowTemplate != null); - Debug.Assert(CellTemplate != null); - Debug.Assert(ColumnCount > 0); + Debug.Assert(startAxisCellCount > 0); - Context.CellTemplate = CellTemplate.gameObject; Context.ScrollDirection = Scroller.ScrollDirection; - Context.GetColumnCount = () => ColumnCount; - Context.GetColumnSpacing = () => columnSpacing; + Context.GetGroupCount = () => startAxisCellCount; + Context.GetStartAxisSpacing = () => startAxisSpacing; + Context.GetCellSize = () => Scroller.ScrollDirection == ScrollDirection.Horizontal + ? cellSize.y + : cellSize.x; + + SetupCellTemplate(); } /// - /// 行オブジェクトのセットアップを行います. + /// 最初にセルが生成される直前に呼び出されます. + /// メソッドを使用してセルテンプレートのセットアップを行ってください. /// - /// 行を構成する GameObject. - protected virtual GameObject SetupRowTemplate() + /// + /// + /// { + /// class CellGroup : DefaultCellGroup { } + /// + /// [SerializeField] Cell cellPrefab = default; + /// + /// protected override void SetupCellTemplate() => Setup(cellPrefab); + /// } + /// ]]> + /// + protected abstract void SetupCellTemplate(); + + /// + /// セルテンプレートのセットアップを行います. + /// + /// セルのテンプレート. + /// セルグループの型. + protected virtual void Setup(FancyCell cellTemplate) + where TGroup : FancyCell { - var cell = CellTemplate.GetComponent(); - var row = RowTemplate.GetComponent(); + Context.CellTemplate = cellTemplate.gameObject; - 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; + cellGroupTemplate = new GameObject("Group").AddComponent().gameObject; + cellGroupTemplate.transform.SetParent(cellContainer, false); + cellGroupTemplate.SetActive(false); } /// @@ -92,15 +121,26 @@ namespace UnityEngine.UI.Extensions { DataCount = items.Count; - var rows = items + var itemGroups = items .Select((item, index) => (item, index)) .GroupBy( - x => x.index / ColumnCount, + x => x.index / startAxisCellCount, x => x.item) .Select(group => group.ToArray()) .ToArray(); - UpdateContents(rows); + UpdateContents(itemGroups); + } + + /// + /// 指定したアイテムの位置までジャンプします. + /// + /// アイテムのインデックス. + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). + protected override void JumpTo(int itemIndex, float alignment = 0.5f) + { + var groupIndex = itemIndex / startAxisCellCount; + base.JumpTo(groupIndex, alignment); } /// @@ -108,12 +148,12 @@ namespace UnityEngine.UI.Extensions /// /// アイテムのインデックス. /// 移動にかける秒数. - /// . + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). /// 移動が完了した際に呼び出されるコールバック. - public override void ScrollTo(int itemIndex, float duration, Alignment alignment = Alignment.Center, Action onComplete = null) + protected override void ScrollTo(int itemIndex, float duration, float alignment = 0.5f, Action onComplete = null) { - var rowIndex = itemIndex / Context.GetColumnCount(); - base.ScrollTo(rowIndex, duration, alignment, onComplete); + var groupIndex = itemIndex / startAxisCellCount; + base.ScrollTo(groupIndex, duration, alignment, onComplete); } /// @@ -122,23 +162,20 @@ namespace UnityEngine.UI.Extensions /// アイテムのインデックス. /// 移動にかける秒数. /// 移動に使用するイージング. - /// . + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). /// 移動が完了した際に呼び出されるコールバック. - public override void ScrollTo(int itemIndex, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null) + protected override void ScrollTo(int itemIndex, float duration, Ease easing, float alignment = 0.5f, 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); + var groupIndex = itemIndex / startAxisCellCount; + base.ScrollTo(groupIndex, duration, easing, alignment, onComplete); } } + + /// + /// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス. + /// 無限スクロールおよびスナップには対応していません. + /// + /// アイテムのデータ型. + /// + public abstract class FancyGridView : FancyGridView { } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs new file mode 100644 index 0000000..0e78f0f --- /dev/null +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs @@ -0,0 +1,42 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +namespace UnityEngine.UI.Extensions +{ + /// + /// のセルを実装するための抽象基底クラス. + /// が不要な場合は + /// 代わりに を使用します. + /// + /// アイテムのデータ型. + /// の型. + public abstract class FancyGridViewCell : FancyScrollRectCell + where TContext : class, IFancyGridViewContext, new() + { + /// + protected override void UpdatePosition(float normalizedPosition, float localPosition) + { + var cellSize = Context.GetCellSize(); + var spacing = Context.GetStartAxisSpacing(); + var groupCount = Context.GetGroupCount(); + + var indexInGroup = Index % groupCount; + var positionInGroup = (cellSize + spacing) * (indexInGroup - (groupCount - 1) * 0.5f); + + transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal + ? new Vector2(-localPosition, -positionInGroup) + : new Vector2(positionInGroup, localPosition); + } + } + + /// + /// のセルを実装するための抽象基底クラス. + /// + /// アイテムのデータ型. + /// + public abstract class FancyGridViewCell : FancyGridViewCell + { + /// + public sealed override void SetContext(FancyGridViewContext context) => base.SetContext(context); + } +} \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs.meta similarity index 83% rename from Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta rename to Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs.meta index dbcb384..8320c0a 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs.meta +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewCell.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 84300901ad8704c11b39587ed6d87468 +guid: ab8a59bbf5118824ab084e32342ad86b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs index 05090c8..eb5de46 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewContext.cs @@ -8,16 +8,13 @@ namespace UnityEngine.UI.Extensions /// /// のコンテキスト基底クラス. /// - public class FancyGridViewContext : IFancyGridViewContext, IFancyScrollRectContext + public class FancyGridViewContext : IFancyGridViewContext { + ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; } 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; } + GameObject IFancyCellGroupContext.CellTemplate { get; set; } + Func IFancyCellGroupContext.GetGroupCount { get; set; } + Func IFancyGridViewContext.GetStartAxisSpacing { get; set; } + Func IFancyGridViewContext.GetCellSize { get; set; } } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs deleted file mode 100644 index 3613bce..0000000 --- a/Runtime/Scripts/Layout/FancyScrollView/GridView/FancyGridViewRow.cs +++ /dev/null @@ -1,81 +0,0 @@ -/// 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/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs new file mode 100644 index 0000000..07854ce --- /dev/null +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs @@ -0,0 +1,16 @@ +/// Credit setchi (https://github.com/setchi) +/// Sourced from - https://github.com/setchi/FancyScrollView + +using System; + +namespace UnityEngine.UI.Extensions +{ + /// + /// のコンテキストインターフェース. + /// + public interface IFancyCellGroupContext + { + GameObject CellTemplate { get; set; } + Func GetGroupCount { get; set; } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs.meta b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs.meta new file mode 100644 index 0000000..1733d84 --- /dev/null +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyCellGroupContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1dc086f250206754aa8449e252d50388 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs index 70221da..33adb1b 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/GridView/IFancyGridViewContext.cs @@ -8,11 +8,9 @@ namespace UnityEngine.UI.Extensions /// /// のコンテキストインターフェース. /// - public interface IFancyGridViewContext + public interface IFancyGridViewContext : IFancyScrollRectContext, IFancyCellGroupContext { - GameObject CellTemplate { get; set; } - ScrollDirection ScrollDirection { get; set; } - Func GetColumnCount { get; set; } - Func GetColumnSpacing { get; set; } + Func GetStartAxisSpacing { get; set; } + Func GetCellSize { get; set ; } } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs deleted file mode 100644 index d3c1566..0000000 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/Alignment.cs +++ /dev/null @@ -1,12 +0,0 @@ -/// Credit setchi (https://github.com/setchi) -/// Sourced from - https://github.com/setchi/FancyScrollView - -namespace UnityEngine.UI.Extensions -{ - public enum Alignment - { - Head, - Center, - Tail, - } -} \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs index 54b5a66..57ba6bb 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRect.cs @@ -39,10 +39,15 @@ namespace UnityEngine.UI.Extensions [SerializeField] protected float paddingTail = 0f; /// - /// セル同士の余白. + /// スクロール軸方向のセル同士の余白. /// [SerializeField] protected float spacing = 0f; + /// + /// セルのサイズ. + /// + protected abstract float CellSize { get; } + /// /// スクロール可能かどうか. /// @@ -51,13 +56,6 @@ namespace UnityEngine.UI.Extensions /// protected virtual bool Scrollable => MaxScrollPosition > 0f; - /// - /// セルのサイズ. - /// - protected virtual float CellSize => Scroller.ScrollDirection == ScrollDirection.Horizontal - ? CellRectTransform.rect.width - : CellRectTransform.rect.height; - Scroller cachedScroller; /// @@ -68,9 +66,6 @@ namespace UnityEngine.UI.Extensions /// protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent()); - RectTransform cachedCellRect; - RectTransform CellRectTransform => cachedCellRect ?? (cachedCellRect = CellPrefab.transform as RectTransform); - float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f; float ViewportLength => ScrollLength - reuseCellMarginCount * 2f; @@ -87,6 +82,7 @@ namespace UnityEngine.UI.Extensions { base.Initialize(); + Context.ScrollDirection = Scroller.ScrollDirection; Context.CalculateScrollSize = () => { var interval = CellSize + spacing; @@ -138,6 +134,14 @@ namespace UnityEngine.UI.Extensions base.Refresh(); } + /// + protected override void Relayout() + { + AdjustCellIntervalAndScrollOffset(); + RefreshScroller(); + base.Relayout(); + } + /// /// の各種状態を更新します. /// @@ -172,17 +176,17 @@ namespace UnityEngine.UI.Extensions /// スクロール位置. protected new void UpdatePosition(float position) { - UpdatePosition(position, Alignment.Center); + Scroller.Position = ToScrollerPosition(position, 0.5f); } /// - /// スクロール位置を更新します. + /// 指定したアイテムの位置までジャンプします. /// - /// スクロール位置. - /// . - protected virtual void UpdatePosition(float position, Alignment alignment) + /// アイテムのインデックス. + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). + protected virtual void JumpTo(int itemIndex, float alignment = 0.5f) { - Scroller.Position = ToScrollerPosition(position, alignment); + Scroller.Position = ToScrollerPosition(itemIndex, alignment); } /// @@ -190,9 +194,9 @@ namespace UnityEngine.UI.Extensions /// /// アイテムのインデックス. /// 移動にかける秒数. - /// . + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). /// 移動が完了した際に呼び出されるコールバック. - public virtual void ScrollTo(int index, float duration, Alignment alignment = Alignment.Center, Action onComplete = null) + protected virtual void ScrollTo(int index, float duration, float alignment = 0.5f, Action onComplete = null) { Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete); } @@ -203,9 +207,9 @@ namespace UnityEngine.UI.Extensions /// アイテムのインデックス. /// 移動にかける秒数. /// 移動に使用するイージング. - /// . + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). /// 移動が完了した際に呼び出されるコールバック. - public virtual void ScrollTo(int index, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null) + 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); } @@ -244,25 +248,15 @@ namespace UnityEngine.UI.Extensions /// が扱うスクロール位置を が扱うスクロール位置に変換します. /// /// が扱うスクロール位置. - /// . + /// ビューポート内におけるセル位置の基準. 0f(先頭) ~ 1f(末尾). /// が扱うスクロール位置. - protected float ToScrollerPosition(float position, Alignment alignment = Alignment.Center) + protected float ToScrollerPosition(float position, float alignment = 0.5f) { - var offset = (ScrollLength - (1f + reuseCellMarginCount * 2f)) * GetAnchore(alignment); + var offset = alignment * (ScrollLength - (1f + reuseCellMarginCount * 2f)) + + (1f - alignment - 0.5f) * spacing / (CellSize + spacing); return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition)); } - float GetAnchore(Alignment alignment) - { - switch (alignment) - { - case Alignment.Head: return 0.0f; - case Alignment.Center: return 0.5f; - case Alignment.Tail: return 1.0f; - default: return GetAnchore(Alignment.Center); - } - } - /// /// 指定された設定を実現するための /// と @@ -277,10 +271,7 @@ namespace UnityEngine.UI.Extensions protected virtual void OnValidate() { - if (CellPrefab) - { - AdjustCellIntervalAndScrollOffset(); - } + AdjustCellIntervalAndScrollOffset(); if (loop) { diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs index 4e368c9..4b25d5b 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectCell.cs @@ -5,12 +5,12 @@ namespace UnityEngine.UI.Extensions { /// /// のセルを実装するための抽象基底クラス. - /// が不要な場合は + /// が不要な場合は /// 代わりに を使用します. /// /// アイテムのデータ型. - /// の型. - public abstract class FancyScrollRectCell : FancyScrollViewCell + /// の型. + public abstract class FancyScrollRectCell : FancyCell where TContext : class, IFancyScrollRectContext, new() { /// @@ -18,24 +18,29 @@ namespace UnityEngine.UI.Extensions { var (scrollSize, reuseMargin) = Context.CalculateScrollSize(); - var unclampedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f); + var normalizedPosition = (Mathf.Lerp(0f, scrollSize, position) - reuseMargin) / (scrollSize - reuseMargin * 2f); var start = 0.5f * scrollSize; var end = -start; - UpdatePosition(unclampedPosition, Mathf.Lerp(start, end, position)); + UpdatePosition(normalizedPosition, Mathf.Lerp(start, end, position)); } /// /// このセルの位置を更新します. /// - /// + /// /// ビューポートの範囲で正規化されたスクロール位置. /// の値に基づいて /// 0.0 ~ 1.0 の範囲を超えた値が渡されることがあります. /// - /// ローカル位置. - protected virtual void UpdatePosition(float position, float viewportPosition) { } + /// ローカル位置. + protected virtual void UpdatePosition(float normalizedPosition, float localPosition) + { + transform.localPosition = Context.ScrollDirection == ScrollDirection.Horizontal + ? new Vector2(-localPosition, 0) + : new Vector2(0, localPosition); + } } /// @@ -46,6 +51,6 @@ namespace UnityEngine.UI.Extensions public abstract class FancyScrollRectCell : FancyScrollRectCell { /// - public sealed override void SetupContext(FancyScrollRectContext context) => base.SetupContext(context); + public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context); } -} \ No newline at end of file +} diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs index 17c113f..8b1b0c3 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/FancyScrollRectContext.cs @@ -10,6 +10,7 @@ namespace UnityEngine.UI.Extensions /// public class FancyScrollRectContext : IFancyScrollRectContext { + ScrollDirection IFancyScrollRectContext.ScrollDirection { get; set; } Func<(float ScrollSize, float ReuseMargin)> IFancyScrollRectContext.CalculateScrollSize { get; set; } } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs index 0a83479..e40396e 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/ScrollRect/IFancyScrollRectContext.cs @@ -10,6 +10,7 @@ namespace UnityEngine.UI.Extensions /// public interface IFancyScrollRectContext { + ScrollDirection ScrollDirection { get; set; } Func<(float ScrollSize, float ReuseMargin)> CalculateScrollSize { get; set; } } } \ No newline at end of file diff --git a/Runtime/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs b/Runtime/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs index e6b1626..9828a81 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/Scroller/EasingCore.cs @@ -1,30 +1,8 @@ -// -// EasingCore - https://github.com/setchi/EasingCore -// -// The MIT License (MIT) -// -// Copyright (c) 2019 setchi -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -using System; -using UnityEngine; +/* + * EasingCore (https://github.com/setchi/EasingCore) + * Copyright (c) 2020 setchi + * Licensed under MIT (https://github.com/setchi/EasingCore/blob/master/LICENSE) + */ namespace UnityEngine.UI.Extensions.EasingCore { @@ -63,14 +41,16 @@ namespace UnityEngine.UI.Extensions.EasingCore InOutSine, } - public static class EasingFunction + public delegate float EasingFunction(float t); + + public static class Easing { /// /// Gets the easing function /// /// Ease type /// Easing function - public static Func Get(Ease type) + public static EasingFunction Get(Ease type) { switch (type) { @@ -170,7 +150,7 @@ namespace UnityEngine.UI.Extensions.EasingCore Mathf.Approximately(0.0f, v) || Mathf.Approximately(1.0f, v) ? v : v < 0.5f - ? 0.5f * Mathf.Pow(2f, (20f * v) - 10f) + ? 0.5f * Mathf.Pow(2f, (20f * v) - 10f) : -0.5f * Mathf.Pow(2f, (-20f * v) + 10f) + 1f; float inQuad(float t) => t * t; @@ -179,7 +159,7 @@ namespace UnityEngine.UI.Extensions.EasingCore float inOutQuad(float t) => t < 0.5f - ? 2f * t * t + ? 2f * t * t : -2f * t * t + 4f * t - 1f; float inQuart(float t) => t * t * t * t; diff --git a/Runtime/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs b/Runtime/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs index 262cabc..ea0c276 100644 --- a/Runtime/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs +++ b/Runtime/Scripts/Layout/FancyScrollView/Scroller/Scroller.cs @@ -10,7 +10,7 @@ namespace UnityEngine.UI.Extensions /// /// スクロール位置の制御を行うコンポーネント. /// - public class Scroller : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler + public class Scroller : UIBehaviour, IPointerUpHandler, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler, IScrollHandler { [SerializeField] RectTransform viewport = default; @@ -83,9 +83,7 @@ namespace UnityEngine.UI.Extensions set => decelerationRate = value; } - [SerializeField] - Snap snap = new Snap - { + [SerializeField] Snap snap = new Snap { Enable = true, VelocityThreshold = 0.5f, Duration = 0.3f, @@ -151,6 +149,8 @@ namespace UnityEngine.UI.Extensions int totalCount; + bool hold; + bool scrolling; bool dragging; float velocity; @@ -163,14 +163,14 @@ namespace UnityEngine.UI.Extensions public Ease Easing; } - static readonly Func DefaultEasingFunction = EasingFunction.Get(Ease.OutCubic); + static readonly EasingFunction DefaultEasingFunction = Easing.Get(Ease.OutCubic); class AutoScrollState { public bool Enable; public bool Elastic; public float Duration; - public Func EasingFunction; + public EasingFunction EasingFunction; public float StartTime; public float EndPosition; @@ -240,7 +240,7 @@ namespace UnityEngine.UI.Extensions /// 移動にかける秒数. /// 移動に使用するイージング. /// 移動が完了した際に呼び出されるコールバック. - public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, EasingFunction.Get(easing), onComplete); + public void ScrollTo(float position, float duration, Ease easing, Action onComplete = null) => ScrollTo(position, duration, Easing.Get(easing), onComplete); /// /// 指定した位置まで移動します. @@ -249,7 +249,7 @@ namespace UnityEngine.UI.Extensions /// 移動にかける秒数. /// 移動に使用するイージング関数. /// 移動が完了した際に呼び出されるコールバック. - public void ScrollTo(float position, float duration, Func easingFunction, Action onComplete = null) + public void ScrollTo(float position, float duration, EasingFunction easingFunction, Action onComplete = null) { if (duration <= 0f) { @@ -283,13 +283,8 @@ namespace UnityEngine.UI.Extensions throw new ArgumentOutOfRangeException(nameof(index)); } - autoScrollState.Reset(); - - velocity = 0f; - dragging = false; - UpdateSelection(index); - UpdatePosition(index); + Position = index; } /// @@ -311,6 +306,75 @@ namespace UnityEngine.UI.Extensions : MovementDirection.Down; } + /// + void IPointerDownHandler.OnPointerDown(PointerEventData eventData) + { + if (!draggable || eventData.button != PointerEventData.InputButton.Left) + { + return; + } + + hold = true; + velocity = 0f; + autoScrollState.Reset(); + } + + /// + void IPointerUpHandler.OnPointerUp(PointerEventData eventData) + { + if (!draggable || eventData.button != PointerEventData.InputButton.Left) + { + return; + } + + if (hold && snap.Enable) + { + UpdateSelection(Mathf.Clamp(Mathf.RoundToInt(currentPosition), 0, totalCount - 1)); + ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing); + } + + hold = false; + } + + /// + void IScrollHandler.OnScroll(PointerEventData eventData) + { + if (!draggable) + { + return; + } + + var delta = eventData.scrollDelta; + + // Down is positive for scroll events, while in UI system up is positive. + delta.y *= -1; + var scrollDelta = scrollDirection == ScrollDirection.Horizontal + ? Mathf.Abs(delta.y) > Mathf.Abs(delta.x) + ? delta.y + : delta.x + : Mathf.Abs(delta.x) > Mathf.Abs(delta.y) + ? delta.x + : delta.y; + + if (eventData.IsScrolling()) + { + scrolling = true; + } + + var position = currentPosition + scrollDelta / ViewportSize * scrollSensitivity; + if (movementType == MovementType.Clamped) + { + position += CalculateOffset(position); + } + + if (autoScrollState.Enable) + { + autoScrollState.Reset(); + } + + UpdatePosition(position); + } + /// void IBeginDragHandler.OnBeginDrag(PointerEventData eventData) { @@ -319,6 +383,7 @@ namespace UnityEngine.UI.Extensions return; } + hold = false; RectTransformUtility.ScreenPointToLocalPointInRectangle( viewport, eventData.position, @@ -449,7 +514,7 @@ namespace UnityEngine.UI.Extensions UpdatePosition(position); } - else if (!dragging && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f))) + else if (!(dragging || scrolling) && (!Mathf.Approximately(offset, 0f) || !Mathf.Approximately(velocity, 0f))) { var position = currentPosition; @@ -500,13 +565,14 @@ namespace UnityEngine.UI.Extensions } } - if (!autoScrollState.Enable && dragging && inertia) + if (!autoScrollState.Enable && (dragging || scrolling) && inertia) { var newVelocity = (currentPosition - prevPosition) / deltaTime; velocity = Mathf.Lerp(velocity, newVelocity, deltaTime * 10f); } prevPosition = currentPosition; + scrolling = false; } float CalculateMovementAmount(float sourcePosition, float destPosition)