More progress

This commit is contained in:
Sinai
2021-04-30 21:34:50 +10:00
parent 0bc14b2f76
commit 2378925a8b
27 changed files with 1401 additions and 846 deletions

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using UnityExplorer;
using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
{
// A Slider Scrollbar which automatically resizes for the content size (no pooling).
// Currently just used for the C# Console input field.
public class AutoSliderScrollbar : UIBehaviourModel
{
public override GameObject UIRoot
{
get
{
if (Slider)
return Slider.gameObject;
return null;
}
}
//public event Action<float> OnValueChanged;
internal readonly Scrollbar Scrollbar;
internal readonly Slider Slider;
internal RectTransform ContentRect;
internal RectTransform ViewportRect;
//internal InputFieldScroller m_parentInputScroller;
public AutoSliderScrollbar(Scrollbar scrollbar, Slider slider, RectTransform contentRect, RectTransform viewportRect)
{
this.Scrollbar = scrollbar;
this.Slider = slider;
this.ContentRect = contentRect;
this.ViewportRect = viewportRect;
this.Scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
this.Slider.onValueChanged.AddListener(this.OnSliderValueChanged);
//this.RefreshVisibility();
this.Slider.Set(0f, false);
}
private float lastAnchorPosition;
private float lastContentHeight;
private float lastViewportHeight;
private bool _refreshWanted;
public override void Update()
{
if (!Enabled)
return;
_refreshWanted = false;
if (ContentRect.localPosition.y != lastAnchorPosition)
{
lastAnchorPosition = ContentRect.localPosition.y;
_refreshWanted = true;
}
if (ContentRect.rect.height != lastContentHeight)
{
lastContentHeight = ContentRect.rect.height;
_refreshWanted = true;
}
if (ViewportRect.rect.height != lastViewportHeight)
{
lastViewportHeight = ViewportRect.rect.height;
_refreshWanted = true;
}
if (_refreshWanted)
UpdateSliderHandle();
}
public void UpdateSliderHandle()
{
// calculate handle size based on viewport / total data height
var totalHeight = ContentRect.rect.height;
var viewportHeight = ViewportRect.rect.height;
if (totalHeight <= viewportHeight)
{
Slider.value = 0f;
Slider.interactable = false;
return;
}
var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight);
handleHeight = Math.Max(15f, handleHeight);
// resize the handle container area for the size of the handle (bigger handle = smaller container)
var container = Slider.m_HandleContainerRect;
container.offsetMax = new Vector2(container.offsetMax.x, -(handleHeight * 0.5f));
container.offsetMin = new Vector2(container.offsetMin.x, handleHeight * 0.5f);
// set handle size
Slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, handleHeight);
// if slider is 100% height then make it not interactable
Slider.interactable = !Mathf.Approximately(handleHeight, viewportHeight);
float val = 0f;
if (totalHeight > 0f)
val = (float)((decimal)ContentRect.localPosition.y / (decimal)(totalHeight - ViewportRect.rect.height));
Slider.value = val;
}
public void OnScrollbarValueChanged(float value)
{
value = 1f - value;
if (this.Slider.value != value)
this.Slider.Set(value, false);
//OnValueChanged?.Invoke(value);
}
public void OnSliderValueChanged(float value)
{
value = 1f - value;
this.Scrollbar.value = value;
//OnValueChanged?.Invoke(value);
}
public override void ConstructUI(GameObject parent)
{
throw new NotImplementedException();
}
}
}

View File

@ -19,106 +19,93 @@ namespace UnityExplorer.UI.Utility
{
get
{
if (inputField)
return inputField.gameObject;
if (InputField)
return InputField.gameObject;
return null;
}
}
internal SliderScrollbar sliderScroller;
internal InputField inputField;
internal AutoSliderScrollbar Slider;
internal InputField InputField;
internal RectTransform inputRect;
internal LayoutElement layoutElement;
internal VerticalLayoutGroup parentLayoutGroup;
internal RectTransform ContentRect;
internal RectTransform ViewportRect;
internal static CanvasScaler canvasScaler;
internal static CanvasScaler RootScaler;
public InputFieldScroller(SliderScrollbar sliderScroller, InputField inputField)
public InputFieldScroller(AutoSliderScrollbar sliderScroller, InputField inputField)
{
//Instances.Add(this);
this.sliderScroller = sliderScroller;
this.inputField = inputField;
sliderScroller.m_parentInputScroller = this;
this.Slider = sliderScroller;
this.InputField = inputField;
inputField.onValueChanged.AddListener(OnTextChanged);
inputRect = inputField.GetComponent<RectTransform>();
layoutElement = inputField.gameObject.AddComponent<LayoutElement>();
parentLayoutGroup = inputField.transform.parent.GetComponent<VerticalLayoutGroup>();
ContentRect = inputField.GetComponent<RectTransform>();
ViewportRect = ContentRect.transform.parent.GetComponent<RectTransform>();
layoutElement.minHeight = 25;
layoutElement.minWidth = 100;
if (!canvasScaler)
canvasScaler = UIManager.CanvasRoot.GetComponent<CanvasScaler>();
if (!RootScaler)
RootScaler = UIManager.CanvasRoot.GetComponent<CanvasScaler>();
}
internal string m_lastText;
internal bool m_updateWanted;
// only done once, to fix height on creation.
internal bool heightInitAfterLayout;
internal bool m_wantJumpToBottom;
private float m_desiredContentHeight;
public override void Update()
{
if (!heightInitAfterLayout)
{
heightInitAfterLayout = true;
var height = sliderScroller.m_scrollRect.parent.parent.GetComponent<RectTransform>().rect.height;
layoutElement.preferredHeight = height;
}
if (m_updateWanted && inputField.gameObject.activeInHierarchy)
if (m_updateWanted)
{
m_updateWanted = false;
RefreshUI();
ProcessInputText();
}
float desiredHeight = Math.Max(m_desiredContentHeight, ViewportRect.rect.height);
if (ContentRect.rect.height < desiredHeight)
{
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
this.Slider.UpdateSliderHandle();
}
else if (ContentRect.rect.height > desiredHeight)
{
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
this.Slider.UpdateSliderHandle();
}
if (m_wantJumpToBottom)
{
Slider.Slider.value = 1f;
m_wantJumpToBottom = false;
}
}
//internal bool CheckDestroyed()
//{
// if (sliderScroller == null || sliderScroller.CheckDestroyed())
// {
// Instances.Remove(this);
// return true;
// }
// return false;
//}
internal void OnTextChanged(string text)
{
m_lastText = text;
m_updateWanted = true;
}
internal void RefreshUI()
internal void ProcessInputText()
{
var curInputRect = inputField.textComponent.rectTransform.rect;
var scaleFactor = canvasScaler.scaleFactor;
var curInputRect = InputField.textComponent.rectTransform.rect;
var scaleFactor = RootScaler.scaleFactor;
// Current text settings
var texGenSettings = inputField.textComponent.GetGenerationSettings(curInputRect.size);
var texGenSettings = InputField.textComponent.GetGenerationSettings(curInputRect.size);
texGenSettings.generateOutOfBounds = false;
texGenSettings.scaleFactor = scaleFactor;
// Preferred text rect height
var textGen = inputField.textComponent.cachedTextGeneratorForLayout;
float preferredHeight = textGen.GetPreferredHeight(m_lastText, texGenSettings) + 10;
var textGen = InputField.textComponent.cachedTextGeneratorForLayout;
m_desiredContentHeight = textGen.GetPreferredHeight(m_lastText, texGenSettings) + 10;
// Default text rect height (fit to scroll parent or expand to fit text)
float minHeight = Mathf.Max(preferredHeight, sliderScroller.m_scrollRect.rect.height - 25);
layoutElement.preferredHeight = minHeight;
if (inputField.caretPosition == inputField.text.Length
&& inputField.text.Length > 0
&& inputField.text[inputField.text.Length - 1] == '\n')
// jump to bottom
if (InputField.caretPosition == InputField.text.Length
&& InputField.text.Length > 0
&& InputField.text[InputField.text.Length - 1] == '\n')
{
sliderScroller.m_slider.value = 0f;
m_wantJumpToBottom = true;
}
}

View File

@ -67,7 +67,7 @@ namespace UnityExplorer.UI.Widgets
// our first cell and they take priority, so reduce our height by
// (minHeight - remainder) to account for that. We need to fill that
// gap and reach the next cell before we take priority.
if (!Mathf.Approximately(rem, 0f))
if (rem != 0.0f)
height -= (DefaultHeight - rem);
return (int)Math.Ceiling((decimal)height / (decimal)DefaultHeight);
@ -139,8 +139,11 @@ namespace UnityExplorer.UI.Widgets
{
if (dataIndex >= ScrollPool.DataSource.ItemCount)
{
while (heightCache.Count > dataIndex)
RemoveLast();
if (heightCache.Count > dataIndex)
{
while (heightCache.Count > dataIndex)
RemoveLast();
}
return;
}
@ -249,14 +252,6 @@ namespace UnityExplorer.UI.Widgets
}
}
}
//// if sister cache is set, then update it too.
//if (SisterCache != null)
//{
// var realIdx = ScrollPool.DataSource.GetRealIndexOfTempIndex(dataIndex);
// if (realIdx >= 0)
// SisterCache.SetIndex(realIdx, height, true);
//}
}
private void RebuildCache()

View File

@ -115,7 +115,7 @@ namespace UnityExplorer.UI.Widgets
if (writingLocked && timeofLastWriteLock < Time.time)
writingLocked = false;
if (prevContentHeight <= 1f && Content?.rect.height > 1f)
if (prevContentHeight <= 1f && Content.rect.height > 1f)
{
prevContentHeight = Content.rect.height;
}
@ -131,10 +131,12 @@ namespace UnityExplorer.UI.Widgets
public void Rebuild()
{
HeightCache = new DataHeightCache<T>(this);
SetRecycleViewBounds(false);
SetScrollBounds();
ExtendCellPool();
CheckExtendCellPool();
writingLocked = false;
Content.anchoredPosition = Vector2.zero;
UpdateSliderHandle(true);
@ -163,6 +165,7 @@ namespace UnityExplorer.UI.Widgets
// Initialize
private bool m_doneFirstInit;
private bool m_initialized;
public void Initialize(IPoolDataSource<T> dataSource)
@ -173,12 +176,16 @@ namespace UnityExplorer.UI.Widgets
HeightCache = new DataHeightCache<T>(this);
DataSource = dataSource;
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
this.slider = ScrollRect.GetComponentInChildren<Slider>();
slider.onValueChanged.AddListener(OnSliderValueChanged);
if (!m_doneFirstInit)
{
m_doneFirstInit = true;
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
this.slider = ScrollRect.GetComponentInChildren<Slider>();
slider.onValueChanged.AddListener(OnSliderValueChanged);
ScrollRect.vertical = true;
ScrollRect.horizontal = false;
ScrollRect.vertical = true;
ScrollRect.horizontal = false;
}
ScrollRect.onValueChanged.RemoveListener(OnValueChangedListener);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
@ -224,12 +231,18 @@ namespace UnityExplorer.UI.Widgets
}
CellPool.Clear();
}
bottomDataIndex = -1;
topPoolIndex = 0;
bottomPoolIndex = 0;
}
private void CreateCellPool(bool andResetDataIndex = true)
private void CreateCellPool()
{
ReturnCells();
CheckDataSourceCountChange(out _);
float currentPoolCoverage = 0f;
float requiredCoverage = ScrollRect.viewport.rect.height + RecycleThreshold;
@ -250,8 +263,7 @@ namespace UnityExplorer.UI.Widgets
currentPoolCoverage += PrototypeHeight;
}
if (andResetDataIndex)
bottomDataIndex = CellPool.Count - 1;
bottomDataIndex = CellPool.Count - 1;
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
@ -266,13 +278,12 @@ namespace UnityExplorer.UI.Widgets
RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold);
if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f)
ExtendCellPool();
CheckExtendCellPool();
prevViewportHeight = Viewport.rect.height;
}
private bool ExtendCellPool()
private bool CheckExtendCellPool()
{
CheckDataSourceCountChange(out _);
@ -285,6 +296,7 @@ namespace UnityExplorer.UI.Widgets
bottomDataIndex += cellsRequired;
// TODO sometimes still jumps a litte bit, need to figure out why.
float prevAnchor = Content.localPosition.y;
float prevHeight = Content.rect.height;
@ -299,21 +311,29 @@ namespace UnityExplorer.UI.Widgets
{
int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1));
cell.Rect.SetSiblingIndex(index);
if (bottomPoolIndex == index - 1)
bottomPoolIndex++;
}
}
RefreshCells(true);
//ExplorerCore.Log("Anchor: " + Content.localPosition.y + ", prev: " + prevAnchor);
//ExplorerCore.Log("Height: " + Content.rect.height + ", prev:" + prevHeight);
if (Content.localPosition.y != prevAnchor)
{
var diff = Content.localPosition.y - prevAnchor;
Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y - diff);
}
ScrollRect.UpdatePrevData();
SetScrollBounds();
UpdateSliderHandle(true);
if (Content.rect.height != prevHeight)
{
var diff = Content.rect.height - prevHeight;
//ExplorerCore.Log("Height diff: " + diff);
//Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y - diff);
}
return true;
}
@ -413,6 +433,16 @@ namespace UnityExplorer.UI.Widgets
ScrollRect.UpdatePrevData();
}
private void RefreshCellHeightsFast()
{
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{
var curr = enumerator.Current;
HeightCache.SetIndex(curr.dataIndex, CellPool[curr.cellIndex].Rect.rect.height);
}
}
private void SetCell(T cachedCell, int dataIndex)
{
cachedCell.Enable();
@ -432,6 +462,8 @@ namespace UnityExplorer.UI.Widgets
if (InputManager.MouseScrollDelta != Vector2.zero)
ScrollRect.StopMovement();
RefreshCellHeightsFast();
SetRecycleViewBounds(true);
float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
@ -458,7 +490,7 @@ namespace UnityExplorer.UI.Widgets
SetScrollBounds();
//WritingLocked = true;
UpdateSliderHandle();
UpdateSliderHandle(true);
}
private bool ShouldRecycleTop => GetCellExtent(CellPool[topPoolIndex].Rect) > RecycleViewBounds.x
@ -598,6 +630,8 @@ namespace UnityExplorer.UI.Widgets
ScrollRect.StopMovement();
RefreshCellHeightsFast();
// normalize the scroll position for the scroll bounds.
// this translates the value into saying "point at the center of the height of the viewport"
var scrollHeight = NormalizedScrollBounds.y - NormalizedScrollBounds.x;
@ -630,52 +664,49 @@ namespace UnityExplorer.UI.Widgets
// check if our pool indices contain the desired index. If so, rotate and set
if (bottomDataIndex == desiredBottomIndex)
{
// cells will be the same, do nothing?
// cells will be the same, do nothing
}
else if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
{
// top cell falls within the new range, rotate around that
int rotate = TopDataIndex - poolStartIndex;
for (int i = 0; i < rotate; i++)
{
CellPool[bottomPoolIndex].Rect.SetAsFirstSibling();
//set new indices
topPoolIndex = bottomPoolIndex;
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
bottomDataIndex--;
SetCell(CellPool[topPoolIndex], TopDataIndex);
}
}
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
{
// bottom cells falls within the new range, rotate around that
int rotate = desiredBottomIndex - bottomDataIndex;
for (int i = 0; i < rotate; i++)
{
CellPool[topPoolIndex].Rect.SetAsLastSibling();
//set new indices
bottomPoolIndex = topPoolIndex;
topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
bottomDataIndex++;
SetCell(CellPool[bottomPoolIndex], bottomDataIndex);
}
}
else
{
if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
bottomDataIndex = desiredBottomIndex;
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{
// top cell falls within the new range, rotate around that
int rotate = TopDataIndex - poolStartIndex;
for (int i = 0; i < rotate; i++)
{
CellPool[bottomPoolIndex].Rect.SetAsFirstSibling();
//set new indices
topPoolIndex = bottomPoolIndex;
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
bottomDataIndex--;
SetCell(CellPool[topPoolIndex], TopDataIndex);
}
}
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
{
// bottom cells falls within the new range, rotate around that
int rotate = desiredBottomIndex - bottomDataIndex;
for (int i = 0; i < rotate; i++)
{
CellPool[topPoolIndex].Rect.SetAsLastSibling();
//set new indices
bottomPoolIndex = topPoolIndex;
topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
bottomDataIndex++;
SetCell(CellPool[bottomPoolIndex], bottomDataIndex);
}
}
else
{
bottomDataIndex = desiredBottomIndex;
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
SetCell(cell, curr.dataIndex);
}
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
SetCell(cell, curr.dataIndex);
}
}

View File

@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using UnityExplorer;
using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
{
// Basically just to fix an issue with Scrollbars, instead we use a Slider as the scrollbar.
public class SliderScrollbar : UIBehaviourModel
{
public bool IsActive { get; private set; }
public override GameObject UIRoot
{
get
{
if (m_slider)
return m_slider.gameObject;
return null;
}
}
public event Action<float> OnValueChanged;
internal readonly Scrollbar m_scrollbar;
internal readonly Slider m_slider;
internal readonly RectTransform m_scrollRect;
internal InputFieldScroller m_parentInputScroller;
public SliderScrollbar(Scrollbar scrollbar, Slider slider)
{
this.m_scrollbar = scrollbar;
this.m_slider = slider;
this.m_scrollRect = scrollbar.transform.parent.GetComponent<RectTransform>();
this.m_scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
this.m_slider.onValueChanged.AddListener(this.OnSliderValueChanged);
this.RefreshVisibility();
this.m_slider.Set(1f, false);
}
public override void Update()
{
this.RefreshVisibility();
}
internal void RefreshVisibility()
{
if (!m_slider.gameObject.activeInHierarchy)
{
IsActive = false;
return;
}
bool shouldShow = !Mathf.Approximately(this.m_scrollbar.size, 1);
//var obj = this.m_slider.handleRect.gameObject;
if (IsActive != shouldShow)
{
IsActive = shouldShow;
m_slider.interactable = shouldShow;
if (IsActive)
this.m_slider.Set(this.m_scrollbar.value, false);
else
m_slider.Set(1f, false);
}
}
public void OnScrollbarValueChanged(float _value)
{
if (this.m_slider.value != _value)
this.m_slider.Set(_value, false);
OnValueChanged?.Invoke(_value);
}
public void OnSliderValueChanged(float _value)
{
this.m_scrollbar.value = _value;
OnValueChanged?.Invoke(_value);
}
#region UI CONSTRUCTION
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
{
GameObject sliderObj = UIFactory.CreateUIObject("SliderScrollbar", parent, UIFactory._smallElementSize);
GameObject bgObj = UIFactory.CreateUIObject("Background", sliderObj);
GameObject handleSlideAreaObj = UIFactory.CreateUIObject("Handle Slide Area", sliderObj);
GameObject handleObj = UIFactory.CreateUIObject("Handle", handleSlideAreaObj);
Image bgImage = bgObj.AddComponent<Image>();
bgImage.type = Image.Type.Sliced;
bgImage.color = new Color(0.1f, 0.1f, 0.1f, 1.0f);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
bgRect.offsetMax = new Vector2(0f, 0f);
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
handleSlideRect.anchorMin = new Vector2(0f, 0f);
handleSlideRect.anchorMax = new Vector2(1f, 1f);
handleSlideRect.pivot = new Vector2(0.5f, 0.5f);
handleSlideRect.offsetMin = new Vector2(25f, 30f);
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
handleSlideRect.sizeDelta = new Vector2(-20f, -30f);
Image handleImage = handleObj.AddComponent<Image>();
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
var handleRect = handleObj.GetComponent<RectTransform>();
handleRect.sizeDelta = new Vector2(15f, 30f);
handleRect.offsetMin = new Vector2(-13f, -28f);
handleRect.offsetMax = new Vector2(2f, -2f);
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
sliderBarLayout.minWidth = 25;
sliderBarLayout.flexibleWidth = 0;
sliderBarLayout.minHeight = 30;
sliderBarLayout.flexibleHeight = 5000;
slider = sliderObj.AddComponent<Slider>();
slider.handleRect = handleObj.GetComponent<RectTransform>();
slider.targetGraphic = handleImage;
slider.direction = Slider.Direction.BottomToTop;
RuntimeProvider.Instance.SetColorBlock(slider,
new Color(0.4f, 0.4f, 0.4f),
new Color(0.5f, 0.5f, 0.5f),
new Color(0.3f, 0.3f, 0.3f),
new Color(0.2f, 0.2f, 0.2f));
return sliderObj;
}
public override void ConstructUI(GameObject parent)
{
throw new NotImplementedException();
}
#endregion
}
}