Skip to content

Instantly share code, notes, and snippets.

@skjalgsm
Created February 16, 2021 22:16
Show Gist options
  • Save skjalgsm/1f48219306861081fb6f4b39c971050b to your computer and use it in GitHub Desktop.
Save skjalgsm/1f48219306861081fb6f4b39c971050b to your computer and use it in GitHub Desktop.
/// <summary>Only works on vertical scroll views</summary>
public class TableStickyRow : MonoBehaviour
{
[SerializeField]
RectTransform stickyPrefab = null;
//Todo: Set this in another way
[SerializeField]
private int stickyIndex;
ITableAdapter tableAdapter;
TableParams tableParams;
Type viewHolderType;
TupleViewsHolder stickyRow;
/// <summary>If the header should have a different model-to-view binding logic, use this</summary>
public void InitWithCustomItemViewsHolder<TCustomItemViewsHolder>(ITableAdapter hierarchyOSA)
where TCustomItemViewsHolder : AbstractViewsHolder, ITupleProvider
{
Init(hierarchyOSA, typeof(TCustomItemViewsHolder));
}
public void InitWithSameItemViewsHolder(ITableAdapter hierarchyOSA)
{
Init(hierarchyOSA, hierarchyOSA.GetViewsHolderType());
}
void Init(ITableAdapter adapter, Type viewHolderItemType)
{
tableAdapter = adapter;
viewHolderType = viewHolderItemType;
tableParams = tableAdapter.BaseParameters as TableParams;
tableAdapter.ScrollPositionChanged -= OnScrollPositionChanged;
tableAdapter.ScrollPositionChanged += OnScrollPositionChanged;
if (stickyRow == null)
{
stickyRow = CreateStickyRow();
}
ManageSticky();
}
TupleViewsHolder CreateStickyRow()
{
var instance = Activator.CreateInstance(viewHolderType) as TupleViewsHolder;
var headerAsAbstractVh = instance as AbstractViewsHolder;
// Index is never stored. it's retrieved only on expand/collapse, since it's currently expensive to get it
headerAsAbstractVh.Init(stickyPrefab, tableAdapter.BaseParameters.Viewport, stickyIndex);
headerAsAbstractVh.root.SetAsLastSibling();
#if UNITY_EDITOR
headerAsAbstractVh.root.name = "Sticky Row";
#endif
// Positioning a views holder. Taken from OSA.AddViewsHolderAndMakeVisible() method in OSAInternal.cs (15-Feb-19, 16:33)
var layoutInfo = tableAdapter.GetLayoutInfoReadonly();
SetStickyInset(instance, layoutInfo.startEdge, layoutInfo.transvStartEdge);
DeactivateVH(instance);
return instance;
}
private void OnScrollPositionChanged(double position)
{
if (tableAdapter == null) // not ready yet
return;
ManageSticky();
}
private static void ActivateVH(TupleViewsHolder viewHolder)
{
viewHolder.root.gameObject.SetActive(true);
}
private static void DeactivateVH(TupleViewsHolder viewHolder)
{
viewHolder.root.gameObject.SetActive(false);
}
private void SetStickyInset(TupleViewsHolder sticky, RectTransform.Edge startEdge, RectTransform.Edge transvStartEdge)
{
var layoutInfo = tableAdapter.GetLayoutInfoReadonly();
sticky.root.anchorMin = sticky.root.anchorMax = layoutInfo.constantAnchorPosForAllItems;
// Positioning a views holder. Taken from OSA.AddViewsHolderAndMakeVisible() method in OSAInternal.cs (15-Feb-19, 16:33)
sticky.root.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(startEdge, 0, tableAdapter.BaseParameters.DefaultItemSize);
if (layoutInfo.transversalPaddingContentStart == -1d)
throw new OSAException("transversalPaddingContentStart is not allowed to be -1 when using " + typeof(TableStickyRow).Name);
sticky.root.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(transvStartEdge, (float)layoutInfo.transversalPaddingContentStart, (float)layoutInfo.itemsConstantTransversalSize);
}
private void ManageSticky()
{
// Nothing to show when sticky index is out of bounds
int itemsCount = tableAdapter.GetItemsCount();
if (stickyIndex < 0 || stickyIndex >= itemsCount)
{
DeactivateVH(stickyRow);
return;
}
//Sticky row always shows
ActivateVH(stickyRow);
stickyRow.ItemIndex = stickyIndex;
stickyRow.UpdateViews(tableAdapter.Tuples.GetTuple(stickyIndex), tableAdapter.Columns);
var viewHolder = tableAdapter.GetBaseItemViewsHolderIfVisible(stickyIndex);
if (viewHolder != null)
{
//Debug.Log("view holder is not null (already showing)");
float headerHeight = tableParams.Table.ColumnsTupleSize;
//Debug.Log("headerHeight: " + headerHeight);
float amountBeforeViewport = -tableAdapter.GetItemRealInsetFromParentStart(viewHolder.root);
//Debug.Log("amountBeforeViewport: " + amountBeforeViewport);
bool isRowVisible = amountBeforeViewport <= -headerHeight;
//Debug.Log("isRowVisible: " + isRowVisible + " = " + amountBeforeViewport + " <= " + -headerHeight);
//_StickyRow.root.SetInsetAndSizeFromParentEdge(edge, edge == RectTransform.Edge.Bottom ? 0 : _Params.Table.ColumnsTupleSize, _Params.DefaultItemSize);
float amountAfterViewport = -tableAdapter.GetItemRealInsetFromParentEnd(viewHolder.root);
//Debug.Log("amountAfterViewport" + amountAfterViewport);
isRowVisible &= amountAfterViewport < 0;
if (isRowVisible)
{
//Debug.Log("view holder is not null (already showing), so we disable.");
stickyRow.root.anchorMin = viewHolder.root.anchorMin;
stickyRow.root.anchorMax = viewHolder.root.anchorMax;
stickyRow.root.sizeDelta = viewHolder.root.sizeDelta;
stickyRow.root.anchoredPosition = viewHolder.root.anchoredPosition;
return;
}
}
//Figure out if its going on top or bottom
var firstViewHolder = tableAdapter.GetBaseItemViewsHolder(0);
int firstIndex = firstViewHolder.ItemIndex;
var lastViewHolder = tableAdapter.GetBaseItemViewsHolder(tableAdapter.VisibleItemsCount - 1);
int lastIndex = lastViewHolder.ItemIndex;
int closeToFirstIndex = Mathf.Abs(firstIndex - stickyIndex);
int closeToLastIndex = Mathf.Abs(lastIndex - stickyIndex);
var edge = closeToLastIndex > closeToFirstIndex ? RectTransform.Edge.Top : RectTransform.Edge.Bottom;
stickyRow.root.SetInsetAndSizeFromParentEdge(edge, edge == RectTransform.Edge.Bottom ? 0 : tableParams.Table.ColumnsTupleSize, tableParams.DefaultItemSize);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment