init
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec12c57b1ba12794b93b02fc5ddfe1ab
|
||||
folderAsset: yes
|
||||
timeCreated: 1638561376
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de71ef25b2d6216429424ec0c68a28b8
|
||||
folderAsset: yes
|
||||
timeCreated: 1553129474
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomParams;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.DateTimePicker
|
||||
{
|
||||
/// <summary>Implementing multiple adapters to get a generic picker which returns a <see cref="DateTime"/> object</summary>
|
||||
public class DateTimePickerAdapter : OSA<MyParams, MyItemViewsHolder>
|
||||
{
|
||||
public int SelectedValue { get; private set; }
|
||||
public event Action<int> OnSelectedValueChanged;
|
||||
|
||||
|
||||
#region OSA implementation
|
||||
/// <inheritdoc/>
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!IsInitialized)
|
||||
return;
|
||||
|
||||
if (VisibleItemsCount == 0)
|
||||
return;
|
||||
|
||||
int middleVHIndex = VisibleItemsCount / 2;
|
||||
var middleVH = GetItemViewsHolder(middleVHIndex);
|
||||
|
||||
var prevValue = SelectedValue;
|
||||
SelectedValue = _Params.GetItemValueAtIndex(middleVH.ItemIndex);
|
||||
middleVH.background.CrossFadeColor(_Params.selectedColor, .1f, false, false);
|
||||
|
||||
for (int i = 0; i < VisibleItemsCount; ++i)
|
||||
{
|
||||
if (i != middleVHIndex)
|
||||
GetItemViewsHolder(i).background.CrossFadeColor(_Params.nonSelectedColor, .1f, false, false);
|
||||
}
|
||||
|
||||
if (prevValue != SelectedValue && OnSelectedValueChanged != null)
|
||||
OnSelectedValueChanged(SelectedValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override MyItemViewsHolder CreateViewsHolder(int itemIndex)
|
||||
{
|
||||
var instance = new MyItemViewsHolder();
|
||||
instance.Init(_Params.ItemPrefab, _Params.Content, itemIndex);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void UpdateViewsHolder(MyItemViewsHolder newOrRecycled) { newOrRecycled.titleText.text = _Params.GetItemValueAtIndex(newOrRecycled.ItemIndex) + ""; }
|
||||
#endregion
|
||||
|
||||
void ChangeItemsCountWithChecks(int newCount)
|
||||
{
|
||||
int min = 4;
|
||||
if (newCount < min)
|
||||
newCount = min;
|
||||
|
||||
ResetItems(newCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Serializable] // serializable, so it can be shown in inspector
|
||||
public class MyParams : BaseParamsWithPrefab
|
||||
{
|
||||
public int startItemNumber = 0;
|
||||
public int increment = 1;
|
||||
public Color selectedColor, nonSelectedColor;
|
||||
|
||||
/// <summary>The value of each item is calculated dynamically using its <paramref name="index"/>, <see cref="startItemNumber"/> and the <see cref="increment"/></summary>
|
||||
/// <returns>The item's value (the displayed number)</returns>
|
||||
public int GetItemValueAtIndex(int index) { return startItemNumber + increment * index; }
|
||||
}
|
||||
|
||||
|
||||
public class MyItemViewsHolder : BaseItemViewsHolder
|
||||
{
|
||||
public Image background;
|
||||
public Text titleText;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void CollectViews()
|
||||
{
|
||||
base.CollectViews();
|
||||
|
||||
background = root.GetComponent<Image>();
|
||||
titleText = root.GetComponentInChildren<Text>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1cc6a6937df8c046943043e53538154
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.DateTimePicker
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementing multiple adapters to get a generic picker which returns a <see cref="DateTime"/> object.
|
||||
/// There are 2 ways of using this script: either simply call <see cref="Show(Action{DateTime})"/>
|
||||
/// or drag and drop the prefab from /Resources/Com.ForbiddenByte.OSA/DateTimePickerDialog into your scene and subscribe to <see cref="OnDateSelected"/>
|
||||
/// </summary>
|
||||
public class DateTimePickerDialog : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
bool _AutoInit = false;
|
||||
[SerializeField]
|
||||
bool _DisplaySelectedDateAsShort = false;
|
||||
[SerializeField]
|
||||
bool _DisplaySelectedTimeAsShort = false;
|
||||
[Tooltip("If true, animations won't be affected by Time.timeScale")]
|
||||
[SerializeField]
|
||||
bool _UseUnscaledTime = true;
|
||||
|
||||
public event Action<DateTime> OnDateSelected;
|
||||
|
||||
public DateTimePickerAdapter DayAdapter { get; private set; }
|
||||
public DateTimePickerAdapter MonthAdapter { get; private set; }
|
||||
public DateTimePickerAdapter YearAdapter { get; private set; }
|
||||
|
||||
public DateTimePickerAdapter HourAdapter { get; private set; }
|
||||
public DateTimePickerAdapter MinuteAdapter { get; private set; }
|
||||
public DateTimePickerAdapter SecondAdapter { get; private set; }
|
||||
|
||||
public DateTime SelectedValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DateTime(
|
||||
YearAdapter.SelectedValue, MonthAdapter.SelectedValue, DayAdapter.SelectedValue,
|
||||
HourAdapter.SelectedValue, MinuteAdapter.SelectedValue, SecondAdapter.SelectedValue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const float SCROLL_DURATION1 = .2f;
|
||||
const float SCROLL_DURATION2 = .35f;
|
||||
const float SCROLL_DURATION3 = .5f;
|
||||
const float ANIM_DURATION = .25f;
|
||||
const float DEFAULT_WIDTH = 660f, DEFAULT_HEIGHT = DEFAULT_WIDTH / 2;
|
||||
|
||||
float AnimElapsedTime01 { get { float t = Mathf.Clamp01((Time - _AnimStartTime) / ANIM_DURATION); return t * t * t * t; } }
|
||||
//Vector3 AnimCurrentScale { get { return Vector3.Lerp(_AnimStart, _AnimEnd, AnimElapsedTime01); } }
|
||||
float AnimCurrentFloat { get { return Mathf.Lerp(_AnimStart, _AnimEnd, AnimElapsedTime01); } }
|
||||
|
||||
float Time { get { return _UseUnscaledTime ? UnityEngine.Time.unscaledTime : UnityEngine.Time.time; } }
|
||||
|
||||
Transform _DatePanel, _TimePanel;
|
||||
Text _SelectedDateText, _SelectedTimeText;
|
||||
bool _Initialized;
|
||||
DateTime? _DateToInitWith;
|
||||
bool _Animating;
|
||||
//Vector3 _AnimStart, _AnimEnd;
|
||||
float _AnimStart, _AnimEnd;
|
||||
float _AnimStartTime;
|
||||
Action _ActionOnAnimDone;
|
||||
CanvasGroup _CanvasGroup;
|
||||
DateTimePickerAdapter[] _AllAdapters = new DateTimePickerAdapter[6];
|
||||
|
||||
|
||||
public static DateTimePickerDialog Show(Action<DateTime> onSelected)
|
||||
{
|
||||
return Show(DateTime.Now, onSelected);
|
||||
}
|
||||
|
||||
public static DateTimePickerDialog Show(Action<DateTime> onSelected, string prefabPathInResources)
|
||||
{
|
||||
return Show(DateTime.Now, onSelected, prefabPathInResources);
|
||||
}
|
||||
|
||||
public static DateTimePickerDialog Show(DateTime startingDate, Action<DateTime> onSelected)
|
||||
{
|
||||
return Show(startingDate, onSelected, DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
}
|
||||
|
||||
public static DateTimePickerDialog Show(DateTime startingDate, Action<DateTime> onSelected, string prefabPathInResources)
|
||||
{
|
||||
return Show(startingDate, onSelected, DEFAULT_WIDTH, DEFAULT_HEIGHT, prefabPathInResources);
|
||||
}
|
||||
|
||||
public static DateTimePickerDialog Show(DateTime startingDate, Action<DateTime> onSelected, float width, float height)
|
||||
{
|
||||
var prefabPathInResources = OSAConst.OSA_PATH_IN_RESOURCES + "/" + typeof(DateTimePickerDialog).Name;
|
||||
return Show(startingDate, onSelected, width, height, prefabPathInResources);
|
||||
}
|
||||
|
||||
public static DateTimePickerDialog Show(DateTime startingDate, Action<DateTime> onSelected, float width, float height, string prefabPathInResources)
|
||||
{
|
||||
var go = Resources.Load<GameObject>(prefabPathInResources);
|
||||
var picker = (Instantiate(go) as GameObject).GetComponent<DateTimePickerDialog>();
|
||||
var c = FindObjectOfType<Canvas>();
|
||||
if (!c)
|
||||
throw new OSAException(typeof(DateTimePickerDialog).Name + ": no Canvas was found in the scene");
|
||||
var canvasRT = c.transform as RectTransform;
|
||||
var rt = (picker.transform as RectTransform);
|
||||
rt.SetParent(canvasRT, false);
|
||||
rt.SetAsLastSibling();
|
||||
picker._DateToInitWith = startingDate;
|
||||
if (onSelected != null)
|
||||
picker.OnDateSelected += onSelected;
|
||||
|
||||
rt.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(RectTransform.Edge.Left, (canvasRT.rect.width - width) / 2, width);
|
||||
rt.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(RectTransform.Edge.Top, (canvasRT.rect.height - height) / 2, height);
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_CanvasGroup = GetComponent<CanvasGroup>();
|
||||
_CanvasGroup.alpha = 0f;
|
||||
_AnimEnd = 1f;
|
||||
_AnimStartTime = Time;
|
||||
_Animating = true;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
var adaptersTR = transform.Find("Adapters");
|
||||
_DatePanel = adaptersTR.Find("Date");
|
||||
_DatePanel.GetComponentAtPath("SelectedIndicatorText", out _SelectedDateText);
|
||||
int i = 0;
|
||||
_AllAdapters[i++] = DayAdapter = _DatePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Day");
|
||||
_AllAdapters[i++] = MonthAdapter = _DatePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Month");
|
||||
_AllAdapters[i++] = YearAdapter = _DatePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Year");
|
||||
_TimePanel = adaptersTR.Find("Time");
|
||||
_TimePanel.GetComponentAtPath("SelectedIndicatorText", out _SelectedTimeText);
|
||||
_AllAdapters[i++] = HourAdapter = _TimePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Hour");
|
||||
_AllAdapters[i++] = MinuteAdapter = _TimePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Minute");
|
||||
_AllAdapters[i++] = SecondAdapter = _TimePanel.GetComponentAtPath<DateTimePickerAdapter>("Panel/Second");
|
||||
|
||||
for (i = 0; i < _AllAdapters.Length; ++i)
|
||||
_AllAdapters[i].Parameters.UseUnscaledTime = _UseUnscaledTime;
|
||||
|
||||
if (_AutoInit)
|
||||
ExecuteAfter(.2f, AutoInit);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_Animating)
|
||||
{
|
||||
//transform.localScale = AnimCurrentFloat;
|
||||
_CanvasGroup.alpha = AnimCurrentFloat;
|
||||
|
||||
if (AnimElapsedTime01 == 1f)
|
||||
{
|
||||
_Animating = false;
|
||||
if (_ActionOnAnimDone != null)
|
||||
_ActionOnAnimDone();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_Initialized)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var current = SelectedValue;
|
||||
_SelectedDateText.text = _DisplaySelectedDateAsShort ? current.ToShortDateString() : current.ToLongDateString();
|
||||
_SelectedTimeText.text = _DisplaySelectedTimeAsShort ? current.ToShortTimeString() : current.ToLongTimeString();
|
||||
}
|
||||
catch /*(Exception e)*/{
|
||||
//Debug.Log(e + "\n"+YearAdapter.SelectedValue + "," + MonthAdapter.SelectedValue + "," + DayAdapter.SelectedValue + "," +
|
||||
//HourAdapter.SelectedValue + "," + MinuteAdapter.SelectedValue + "," + SecondAdapter.SelectedValue);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitWithNow() { InitWithDate(DateTime.Now); }
|
||||
|
||||
public void InitWithDate(DateTime dateTime)
|
||||
{
|
||||
StopAnimations();
|
||||
_DateToInitWith = null;
|
||||
UnregisterAutoCorrection();
|
||||
|
||||
int doneNum = 0;
|
||||
int targetDone = 2;
|
||||
Action onDone = () =>
|
||||
{
|
||||
if (++doneNum == targetDone)
|
||||
{
|
||||
_Initialized = true;
|
||||
RegisterAutoCorrection();
|
||||
}
|
||||
};
|
||||
|
||||
//Func<float, bool> onProgress = p01 =>
|
||||
//{
|
||||
// if (p01 == 1f)
|
||||
// {
|
||||
// if (++doneNum == targetDone)
|
||||
// {
|
||||
// _Initialized = true;
|
||||
// RegisterAutoCorrection();
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
//};
|
||||
|
||||
YearAdapter.ResetItems(3000);
|
||||
YearAdapter.SmoothScrollTo(dateTime.Year - 1, SCROLL_DURATION1, .5f, .5f);
|
||||
MonthAdapter.ResetItems(12);
|
||||
MonthAdapter.SmoothScrollTo(dateTime.Month - 1, SCROLL_DURATION2, .5f, .5f);
|
||||
DayAdapter.ResetItems(DateTime.DaysInMonth(dateTime.Year, dateTime.Month));
|
||||
//DayAdapter.SmoothScrollTo(dateTime.Day - 1, SCROLL_DURATION3, .5f, .5f, onProgress, null, true);
|
||||
DayAdapter.SmoothScrollTo(dateTime.Day - 1, SCROLL_DURATION3, .5f, .5f, null, onDone, true);
|
||||
|
||||
SecondAdapter.ResetItems(60);
|
||||
SecondAdapter.SmoothScrollTo(dateTime.Second, SCROLL_DURATION1, .5f, .5f);
|
||||
MinuteAdapter.ResetItems(60);
|
||||
MinuteAdapter.SmoothScrollTo(dateTime.Minute, SCROLL_DURATION2, .5f, .5f);
|
||||
HourAdapter.ResetItems(24);
|
||||
//HourAdapter.SmoothScrollTo(dateTime.Hour, SCROLL_DURATION3, .5f, .5f, onProgress, true);
|
||||
HourAdapter.SmoothScrollTo(dateTime.Hour, SCROLL_DURATION3, .5f, .5f, null, onDone, true);
|
||||
}
|
||||
|
||||
public void ReturnCurrent()
|
||||
{
|
||||
//_AnimStart = Vector3.one;
|
||||
//_AnimEnd = Vector3.zero;
|
||||
_AnimStart = _CanvasGroup.alpha;
|
||||
_AnimEnd = 0f;
|
||||
_AnimStartTime = Time;
|
||||
_Animating = true;
|
||||
|
||||
_ActionOnAnimDone = () =>
|
||||
{
|
||||
_ActionOnAnimDone = null;
|
||||
if (OnDateSelected != null)
|
||||
OnDateSelected(SelectedValue);
|
||||
|
||||
Destroy(gameObject);
|
||||
};
|
||||
}
|
||||
|
||||
void AutoInit()
|
||||
{
|
||||
_DateToInitWith = _DateToInitWith ?? DateTime.Now;
|
||||
InitWithDate(_DateToInitWith.Value);
|
||||
}
|
||||
|
||||
void UnregisterAutoCorrection()
|
||||
{
|
||||
YearAdapter.OnSelectedValueChanged -= OnYearChanged;
|
||||
MonthAdapter.OnSelectedValueChanged -= OnMonthChanged;
|
||||
}
|
||||
|
||||
void RegisterAutoCorrection()
|
||||
{
|
||||
YearAdapter.OnSelectedValueChanged += OnYearChanged;
|
||||
MonthAdapter.OnSelectedValueChanged += OnMonthChanged;
|
||||
}
|
||||
|
||||
void OnYearChanged(int year) { OnMonthChanged(MonthAdapter.SelectedValue); }
|
||||
|
||||
void OnMonthChanged(int month)
|
||||
{
|
||||
var selectedDay = DayAdapter.SelectedValue;
|
||||
int newDaysInMonth = DateTime.DaysInMonth(YearAdapter.SelectedValue, month);
|
||||
if (newDaysInMonth == DayAdapter.GetItemsCount())
|
||||
return;
|
||||
DayAdapter.ResetItems(newDaysInMonth);
|
||||
DayAdapter.ScrollTo(Math.Min(newDaysInMonth, selectedDay) - 1, .5f, .5f);
|
||||
}
|
||||
|
||||
void StopAnimations()
|
||||
{
|
||||
foreach (var adapter in _AllAdapters)
|
||||
{
|
||||
if (adapter)
|
||||
adapter.CancelAllAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExecuteAfter(float seconds, Action action) { StartCoroutine(ExecuteAfterCoroutine(seconds, action)); }
|
||||
IEnumerator ExecuteAfterCoroutine(float seconds, Action action)
|
||||
{
|
||||
if (seconds > 0f)
|
||||
{
|
||||
yield return null;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (_UseUnscaledTime)
|
||||
yield return new WaitForSecondsRealtime(seconds);
|
||||
else
|
||||
yield return new WaitForSeconds(seconds);
|
||||
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67c05838ffd5f8849ad61288be5e976f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e92a4bb33aadd94797256497c81c4c3
|
||||
folderAsset: yes
|
||||
timeCreated: 1638561458
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41e1a65c7b96d03498c2b6c910ff6748
|
||||
folderAsset: yes
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57ccde6a849ccca4188519c929e776d2
|
||||
folderAsset: yes
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.GridView.Specialized.GridWithCategories
|
||||
{
|
||||
public enum CellType
|
||||
{
|
||||
VALID,
|
||||
FOR_ROW_COMPLETION,
|
||||
IN_ROW_SEPARATING_CATEGORIES
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae9c5a81d044ef408cbf2496982fed3
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.GridView.Specialized.GridWithCategories
|
||||
{
|
||||
public class GridWithCategoriesUtil
|
||||
{
|
||||
static T CreateItemModelForRowCompletion<T>(ICategoryModel parentCategory) where T : ICellModel, new()
|
||||
{
|
||||
return new T
|
||||
{
|
||||
ParentCategory = parentCategory,
|
||||
Id = -1,
|
||||
Type = CellType.FOR_ROW_COMPLETION
|
||||
};
|
||||
}
|
||||
|
||||
static T CreateItemModelInRowSeparatingCategories<T>(ICategoryModel parentCategory) where T : ICellModel, new()
|
||||
{
|
||||
return new T
|
||||
{
|
||||
ParentCategory = parentCategory,
|
||||
Id = -1,
|
||||
Type = CellType.IN_ROW_SEPARATING_CATEGORIES
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts your user-level data structure to an OSA Grid-compatible list of items, which will also include "filler" items for the spaces between the categories and empty spaces.
|
||||
/// This approach has the advantage of reusing the OSA's GridAdapter completely, at the expense of creating/managing few additional items at the user-level
|
||||
/// </summary>
|
||||
public static void ConvertCategoriesToListOfItemModels<TCategory, TCell>(int itemSlotsPerRow, List<TCategory> categories, out List<TCell> cells)
|
||||
where TCategory : ICategoryModel
|
||||
where TCell : ICellModel, new()
|
||||
{
|
||||
cells = new List<TCell>();
|
||||
for (int i = 0; i < categories.Count; i++)
|
||||
{
|
||||
var cat = categories[i];
|
||||
|
||||
// Insert an empty row of items to make room for the category's header
|
||||
for (int j = 0; j < itemSlotsPerRow; j++)
|
||||
{
|
||||
var m = CreateItemModelInRowSeparatingCategories<TCell>(cat);
|
||||
cells.Add(m);
|
||||
}
|
||||
|
||||
// Add the actual cells
|
||||
for (int j = 0; j < cat.Count; j++)
|
||||
cells.Add((TCell)cat[j]);
|
||||
|
||||
// If the category's last row is not full, fill it with empty slots, so they won't be occupied with the ones from the next category
|
||||
while (cells.Count % itemSlotsPerRow != 0)
|
||||
cells.Add(CreateItemModelForRowCompletion<TCell>(cat));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3015ac1d7f1fd374ca2280b1b3196003
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.GridView.Specialized.GridWithCategories
|
||||
{
|
||||
public interface ICategoryModel
|
||||
{
|
||||
int Count { get; }
|
||||
ICellModel this[int index] { get; }
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 486ee90ba4b16014ebe95094ecde8c98
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.GridView.Specialized.GridWithCategories
|
||||
{
|
||||
public interface ICellModel
|
||||
{
|
||||
ICategoryModel ParentCategory { get; set; }
|
||||
int Id { get; set; }
|
||||
CellType Type { get; set; }
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0167959a452670147a41bbac4d2b6cc7
|
||||
timeCreated: 1606210561
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab806c65fbd4e8e48aca194d97e8bf4f
|
||||
folderAsset: yes
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02d3576c3b8e35f4eab016ed74dc01b2
|
||||
folderAsset: yes
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
public class BasicColumnInfo : IColumnInfo
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get { return _Name; }
|
||||
set
|
||||
{
|
||||
if (_Name == value)
|
||||
return;
|
||||
|
||||
_Name = value;
|
||||
ReconstructDisplayName();
|
||||
}
|
||||
}
|
||||
public string DisplayName { get; set; }
|
||||
public TableValueType ValueType { get; private set; }
|
||||
public Type EnumValueType { get; private set; }
|
||||
public float Size { get { return _Size; } }
|
||||
|
||||
string _Name;
|
||||
float _Size = -1;
|
||||
|
||||
|
||||
public BasicColumnInfo(string name, TableValueType valueType, Type enumValueType = null, float? customSize = null)
|
||||
{
|
||||
ValueType = valueType;
|
||||
EnumValueType = enumValueType;
|
||||
if (customSize != null)
|
||||
_Size = customSize.Value;
|
||||
|
||||
// Setting it last, so the display name will be reconstructed using the other properties
|
||||
Name = name;
|
||||
}
|
||||
|
||||
void ReconstructDisplayName()
|
||||
{
|
||||
DisplayName = ConstructColumnDisplayName(Name, ValueType, EnumValueType);
|
||||
}
|
||||
|
||||
public static string ConstructColumnDisplayName(string name, TableValueType valueType, Type enumValueType = null)
|
||||
{
|
||||
string innerStr;
|
||||
if (valueType == TableValueType.ENUMERATION)
|
||||
innerStr = "ENUM <i>" + (enumValueType == null ? "<Unknown>" : enumValueType.Name) + "</i>";
|
||||
else
|
||||
innerStr = valueType.ToString();
|
||||
|
||||
return name + "\n<color=#00000070><size=12>" + innerStr + "</size></color>";
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dacb2671ec142e4698dd1539eaeceb4
|
||||
timeCreated: 1563189913
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
public class BasicColumnState : IColumnState
|
||||
{
|
||||
public IColumnInfo Info { get; private set; }
|
||||
public TableValueSortType CurrentSortingType { get; set; }
|
||||
public bool CurrentlyReadOnly { get; set; }
|
||||
public float CurrentSize { get; set; }
|
||||
|
||||
|
||||
public BasicColumnState(IColumnInfo info, bool readonlyByDefault)
|
||||
{
|
||||
Info = info;
|
||||
CurrentSortingType = TableValueSortType.NONE;
|
||||
CurrentlyReadOnly = readonlyByDefault;
|
||||
CurrentSize = info.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbc77a9af44c03a46993cacfdae1d85b
|
||||
timeCreated: 1563189913
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
public class BasicTableColumns : ITuple, ITableColumns
|
||||
{
|
||||
IList<BasicColumnState> _ColumnStates;
|
||||
|
||||
public int ColumnsCount { get { return _ColumnStates.Count; } }
|
||||
int ITuple.Length { get { return ColumnsCount; } }
|
||||
|
||||
|
||||
public BasicTableColumns(IList<IColumnInfo> columnInfos)
|
||||
{
|
||||
_ColumnStates = new List<BasicColumnState>(columnInfos.Count);
|
||||
for (int i = 0; i < columnInfos.Count; i++)
|
||||
_ColumnStates.Add(new BasicColumnState(columnInfos[i], false));
|
||||
}
|
||||
|
||||
public BasicTableColumns(IList<BasicColumnInfo> columnInfos)
|
||||
{
|
||||
_ColumnStates = new List<BasicColumnState>(columnInfos.Count);
|
||||
for (int i = 0; i < columnInfos.Count; i++)
|
||||
_ColumnStates.Add(new BasicColumnState(columnInfos[i], false));
|
||||
}
|
||||
|
||||
|
||||
public IColumnState GetColumnState(int index)
|
||||
{
|
||||
return _ColumnStates[index];
|
||||
}
|
||||
|
||||
public ITuple GetColumnsAsTuple()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>Gets the title of a column</summary>
|
||||
object ITuple.GetValue(int index)
|
||||
{
|
||||
return _ColumnStates[index].Info.DisplayName;
|
||||
}
|
||||
|
||||
/// <summary>Sets the title of a column</summary>
|
||||
void ITuple.SetValue(int index, object value)
|
||||
{
|
||||
_ColumnStates[index].Info.Name = value == null ? "" : value.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the titles of all columns. <paramref name="newValues"/> should be of the same length of the current list,
|
||||
/// i.e. only an existing list of columns can have its names modified
|
||||
/// </summary>
|
||||
void ITuple.ResetValues(IList newValues, bool cloneList)
|
||||
{
|
||||
if (_ColumnStates == null || newValues.Count != _ColumnStates.Count)
|
||||
throw new InvalidOperationException("Not supported for " + typeof(BasicTableColumns).Name + " if the count is different");
|
||||
|
||||
for (int i = 0; i < newValues.Count; i++)
|
||||
(this as ITuple).SetValue(i, newValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3c0e28f9a322c44b98000d7b35cbc35
|
||||
timeCreated: 1562942182
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
/// <summary>
|
||||
/// A table data that's fetched all at once.
|
||||
/// </summary>
|
||||
public class BasicTableData : TableData
|
||||
{
|
||||
public override int Count { get { return _RowTuples.Count; } }
|
||||
public override bool ColumnClearingSupported { get { return Count < TableViewConst.MAX_TABLE_ENTRIES_FOR_ACCEPTABLE_COLUMN_ITERATION_TIME; } }
|
||||
|
||||
IList _RowTuples;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <paramref name="rowTuples"/> is the list of all the rows in the table, as tuples implementing <see cref="ITuple"/>
|
||||
/// </summary>
|
||||
public BasicTableData(ITableColumns columnsProvider, IList rowTuples, bool columnSortingSupported)
|
||||
{
|
||||
_RowTuples = rowTuples;
|
||||
Init(columnsProvider, columnSortingSupported);
|
||||
}
|
||||
|
||||
public override ITuple GetTuple(int index) { return _RowTuples[index] as ITuple; }
|
||||
|
||||
protected override bool ReverseTuplesListIfSupported()
|
||||
{
|
||||
// The ArrayList.Adapter() is an O(1) operation
|
||||
var adapter = ArrayList.Adapter(_RowTuples);
|
||||
adapter.Reverse();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool SortTuplesListIfSupported(IComparer comparerToUse)
|
||||
{
|
||||
// The ArrayList.Adapter() is an O(1) operation
|
||||
var adapter = ArrayList.Adapter(_RowTuples);
|
||||
adapter.Sort(comparerToUse);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f2cf5322da9e7943b78d66a6ba991dd
|
||||
timeCreated: 1563565624
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
public class BasicTableViewOptionsPanel : MonoBehaviour, ITableViewOptionsPanel
|
||||
{
|
||||
[SerializeField]
|
||||
Button _EnterClearingStateButton = null;
|
||||
|
||||
[SerializeField]
|
||||
Button _ExitClearingStateButton = null;
|
||||
|
||||
[SerializeField]
|
||||
GameObject _ClearingStateShownObject = null;
|
||||
|
||||
[SerializeField]
|
||||
GameObject _NoneStateShownObject = null;
|
||||
|
||||
[SerializeField]
|
||||
GameObject _LoadingGameObject = null;
|
||||
|
||||
[SerializeField]
|
||||
CanvasGroup _CanvasGroupToDisableOnLoad = null;
|
||||
|
||||
public bool IsClearing { get { return _IsClearing; } set { SetIsClearing(value); } }
|
||||
public bool IsLoading { get { return _IsLoading; } set { SetIsLoading(value); } }
|
||||
|
||||
bool _IsClearing;
|
||||
bool _IsLoading;
|
||||
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (_EnterClearingStateButton)
|
||||
_EnterClearingStateButton.onClick.AddListener(SetClearing);
|
||||
if (_ExitClearingStateButton)
|
||||
_ExitClearingStateButton.onClick.AddListener(SetNoClearing);
|
||||
|
||||
IsClearing = false;
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_IsLoading)
|
||||
{
|
||||
if (_LoadingGameObject)
|
||||
{
|
||||
_LoadingGameObject.transform.Rotate(Vector3.forward, -270f * Time.deltaTime, Space.Self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetNoClearing()
|
||||
{
|
||||
IsClearing = false;
|
||||
}
|
||||
|
||||
void SetClearing()
|
||||
{
|
||||
IsClearing = true;
|
||||
}
|
||||
|
||||
void SetIsClearing(bool isClearing)
|
||||
{
|
||||
if (_EnterClearingStateButton)
|
||||
_EnterClearingStateButton.gameObject.SetActive(!isClearing);
|
||||
if (_ExitClearingStateButton)
|
||||
_ExitClearingStateButton.gameObject.SetActive(isClearing);
|
||||
if (_ClearingStateShownObject)
|
||||
_ClearingStateShownObject.gameObject.SetActive(isClearing);
|
||||
if (_NoneStateShownObject)
|
||||
_NoneStateShownObject.gameObject.SetActive(!isClearing);
|
||||
|
||||
_IsClearing = isClearing;
|
||||
}
|
||||
|
||||
void SetIsLoading(bool isLoading)
|
||||
{
|
||||
_IsLoading = isLoading;
|
||||
if (_LoadingGameObject)
|
||||
_LoadingGameObject.SetActive(_IsLoading);
|
||||
|
||||
if (_CanvasGroupToDisableOnLoad)
|
||||
_CanvasGroupToDisableOnLoad.blocksRaycasts = !_IsLoading;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e313db585f39b2b4aa28a3dd276b0585
|
||||
timeCreated: 1563526496
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Basic
|
||||
{
|
||||
public class BasicTuple : ITuple
|
||||
{
|
||||
public int Length { get { return _Values.Count; } }
|
||||
|
||||
IList _Values;
|
||||
|
||||
|
||||
/// <summary>See <see cref="ResetValues(IList, bool)"/></summary>
|
||||
public BasicTuple() { }
|
||||
|
||||
/// <summary>See <see cref="ResetValues(IList, bool)"/></summary>
|
||||
public BasicTuple(IList values, bool cloneList = false)
|
||||
{
|
||||
ResetValues(values, cloneList);
|
||||
}
|
||||
|
||||
|
||||
public object GetValue(int index)
|
||||
{
|
||||
return _Values[index];
|
||||
}
|
||||
|
||||
public void SetValue(int index, object value)
|
||||
{
|
||||
_Values[index] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Passing <paramref name="cloneList"/>=true, will clone the list of values. Otherwise, will keep a reference to the list
|
||||
/// and thus will be affected by external changes to it
|
||||
/// </summary>
|
||||
public void ResetValues(IList newValues, bool cloneList)
|
||||
{
|
||||
if (cloneList)
|
||||
{
|
||||
_Values = new object[newValues.Count];
|
||||
for (int i = 0; i < newValues.Count; i++)
|
||||
_Values[i] = newValues[i];
|
||||
}
|
||||
else
|
||||
_Values = newValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33caa64ff4011084b91034f3ba113ca7
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f37b5566452a438468329b3ad4072d24
|
||||
folderAsset: yes
|
||||
timeCreated: 1563565624
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Com.ForbiddenByte.OSA.DataHelpers;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Extra
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for loading chunks of data asynchronously into a <see cref="BufferredTableData"/> and notifying a <see cref="TableAdapter{TParams, TTupleViewsHolder, THeaderTupleViewsHolder}"/>
|
||||
/// about changes, while staying aware of its lifecycle events like <see cref="TableAdapter{TParams, TTupleViewsHolder, THeaderTupleViewsHolder}.ChangeItemsCount(Core.ItemCountChangeMode, int, int, bool, bool)"/>
|
||||
/// and invalidating any existing loading tasks when needed
|
||||
/// </summary>
|
||||
public class AsyncBufferredTableData<TTuple> : ITupleProvider
|
||||
where TTuple : ITuple, new()
|
||||
{
|
||||
public ITableColumns Columns { get; private set; }
|
||||
public int Count { get { return _DataSource.Count; } }
|
||||
public bool ColumnClearingSupported { get { return false; } }
|
||||
public bool ColumnSortingSupported { get { return false; } }
|
||||
public AsyncBufferredDataSource<TTuple> Source { get { return _DataSource; } }
|
||||
|
||||
AsyncBufferredDataSource<TTuple> _DataSource;
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="AsyncBufferredDataSource{T}"/>
|
||||
/// </summary>
|
||||
public AsyncBufferredTableData(ITableColumns columns, int tuplesCount, int chunkBufferSize, AsyncBufferredDataSource<TTuple>.Loader loader)
|
||||
{
|
||||
_DataSource = new AsyncBufferredDataSource<TTuple>(tuplesCount, chunkBufferSize, loader);
|
||||
|
||||
Columns = columns;
|
||||
}
|
||||
|
||||
|
||||
public ITuple GetTuple(int index) { return _DataSource.GetValue(index); }
|
||||
|
||||
//TTuple CreateEmptyTuple()
|
||||
//{
|
||||
// return TableViewUtil.CreateTupleWithEmptyValues<TTuple>(Columns.ColumnsCount);
|
||||
//}
|
||||
|
||||
public bool ChangeColumnSortType(int columnIndex, TableValueType columnType, TableValueSortType currentSorting, TableValueSortType nextSorting)
|
||||
{ throw new NotSupportedException(); }
|
||||
|
||||
public void SetAllValuesOnColumn(int columnIndex, object sameColumnValueInAllTuples)
|
||||
{ throw new NotSupportedException(); }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1eccc520d353e75408cd8e73a89d6fa2
|
||||
timeCreated: 1563617598
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.DataHelpers;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Extra
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for loading data via a <see cref="AsyncBufferredTableData{TTuple}"/> and notifying a <see cref="TableAdapter{TParams, TTupleViewsHolder, THeaderTupleViewsHolder}"/>
|
||||
/// about changes, while staying aware of its lifecycle events like <see cref="TableAdapter{TParams, TTupleViewsHolder, THeaderTupleViewsHolder}.ChangeItemsCount(Core.ItemCountChangeMode, int, int, bool, bool)"/>
|
||||
/// and invalidating any existing loading tasks when needed.
|
||||
/// It'll dispose everything on first count change.
|
||||
/// </summary>
|
||||
public class AsyncLoadingUIController<TTuple>
|
||||
where TTuple : ITuple, new()
|
||||
{
|
||||
ITableAdapter _Adapter;
|
||||
AsyncBufferredTableData<TTuple> _AsyncData;
|
||||
static int _NumInstances; // just for debug purposes
|
||||
|
||||
int _ID = _NumInstances++;
|
||||
|
||||
|
||||
public AsyncLoadingUIController(ITableAdapter tableAdapter, AsyncBufferredTableData<TTuple> data)
|
||||
{
|
||||
_Adapter = tableAdapter;
|
||||
_AsyncData = data;
|
||||
_AsyncData.Source.LoadingSessionStarted += OnAsyncDataLoadingStarted;
|
||||
_AsyncData.Source.SingleTaskFinished += OnAsyncDataSingleTaskFinished;
|
||||
_AsyncData.Source.LoadingSessionEnded += OnAsyncDataLoadingSessionEnded;
|
||||
}
|
||||
|
||||
public void BeginListeningForSelfDisposal()
|
||||
{
|
||||
_Adapter.ItemsRefreshed += OnAdapterItemsRefreshed;
|
||||
}
|
||||
|
||||
void OnAsyncDataLoadingStarted()
|
||||
{
|
||||
if (_Adapter.Options != null)
|
||||
_Adapter.Options.IsLoading = true;
|
||||
}
|
||||
|
||||
void OnAsyncDataSingleTaskFinished(AsyncBufferredDataSource<TTuple>.LoadingTask task)
|
||||
{
|
||||
// Make sure the adapter wasn't disposed meanwhile
|
||||
if (_Adapter == null || !_Adapter.IsInitialized)
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
_Adapter.RefreshRange(task.FirstItemIndex, task.CountToRead);
|
||||
}
|
||||
|
||||
void OnAsyncDataLoadingSessionEnded()
|
||||
{
|
||||
// Make sure the adapter wasn't disposed meanwhile
|
||||
if (_Adapter == null || !_Adapter.IsInitialized)
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Adapter.Options != null)
|
||||
_Adapter.Options.IsLoading = false;
|
||||
}
|
||||
|
||||
// If the adapter refreshes its items while one or more tasks are in progress,
|
||||
// make sure to invalidate them so they'll be ignored when they'll fire OnFinishedOneTask
|
||||
void OnAdapterItemsRefreshed(int prevCount, int newCount)
|
||||
{
|
||||
// When the adapter's items count changes or the views are fully refreshed (for example, as a result
|
||||
// of resizing the ScrollView), it fires the ItemsRefreshed.
|
||||
// Check whether that was a result of a simple Refresh (which is done by
|
||||
// OSA and thus preserves the data) or an external ResetTable call, which changes the data and thus
|
||||
// requires this uiController to self-dispose.
|
||||
bool sameDataReferences = _Adapter.Tuples == _AsyncData && _Adapter.Columns == _AsyncData.Columns;
|
||||
|
||||
if (sameDataReferences)
|
||||
return;
|
||||
|
||||
int numRunning = _AsyncData.Source.CurrentlyLoadingTasksCount;
|
||||
if (numRunning > 0)
|
||||
{
|
||||
//if (_AsyncData.ShowLogs)
|
||||
// Debug.Log("OnAdapterItemsRefreshed(count " + prevCount + " -> " + newCount + ") : Clearing all " + numRunning + " active tasks");
|
||||
|
||||
// Update: this overrides an existing loading task, so it's left to the adapter's will to disable the loading state
|
||||
//if (_Adapter.Options != null)
|
||||
// _Adapter.Options.IsLoading = false;
|
||||
|
||||
_AsyncData.Source.ClearAllRunningTasks();
|
||||
}
|
||||
|
||||
if (_AsyncData.Source.ShowLogs)
|
||||
Debug.Log(
|
||||
"AsyncLoadingUIController #"+ _ID +
|
||||
": OnAdapterItemsRefreshed(count " + prevCount + " -> " + newCount +
|
||||
") : Clearing all " + numRunning + " active tasks and disposing self"
|
||||
);
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
void Dispose()
|
||||
{
|
||||
// Unsubscribing from events makes this object available for GC
|
||||
|
||||
if (_AsyncData != null && _AsyncData.Source != null)
|
||||
{
|
||||
_AsyncData.Source.LoadingSessionStarted -= OnAsyncDataLoadingStarted;
|
||||
_AsyncData.Source.SingleTaskFinished -= OnAsyncDataSingleTaskFinished;
|
||||
_AsyncData.Source.LoadingSessionEnded -= OnAsyncDataLoadingSessionEnded;
|
||||
}
|
||||
if (_Adapter != null)
|
||||
_Adapter.ItemsRefreshed -= OnAdapterItemsRefreshed;
|
||||
|
||||
_Adapter = null;
|
||||
_AsyncData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 564a64a4bd5e3384580f504309027fe3
|
||||
timeCreated: 1563621284
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Com.ForbiddenByte.OSA.DataHelpers;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Extra
|
||||
{
|
||||
/// <summary>
|
||||
/// A table data that's not fetched all at once, but rather on demand, in chunks.
|
||||
/// </summary>
|
||||
public class BufferredTableData : TableData
|
||||
{
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="into">Array to read data into, starting at 0, and ending at countToRead-1</param>
|
||||
/// <param name="firstItemIndex">This is not the index at which to insert the item, but rather the index of the item in your table</param>
|
||||
public delegate void TuplesChunkReader(ITuple[] into, int firstItemIndex, int countToRead);
|
||||
|
||||
public override int Count { get { return _DataSource.Count; } }
|
||||
public override bool ColumnClearingSupported { get { return false; } }
|
||||
|
||||
BufferredDataSource<ITuple> _DataSource;
|
||||
TuplesChunkReader _ChunkReader;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set <paramref name="chunkBufferSize"/> to a smaller value (like 10) if big jumps in the scrolling position are very frequent
|
||||
/// and your data source allows retrieving a few values, but frequently. Default is <see cref="BufferredDataSource{T}.BUFFER_MAX_SIZE_DEFAULT"/>.
|
||||
/// <para>Though the best way is to find it by manually testing different values</para>
|
||||
/// </summary>
|
||||
public BufferredTableData(ITableColumns columnsProvider, int tuplesCount, TuplesChunkReader tuplesChunkReader, int chunkBufferSize, bool columnSortingSupported)
|
||||
{
|
||||
_ChunkReader = tuplesChunkReader;
|
||||
_DataSource = new BufferredDataSource<ITuple>(tuplesCount, ReadTuplesChunk, chunkBufferSize, false /*items will be created directly*/);
|
||||
|
||||
Init(columnsProvider, columnSortingSupported);
|
||||
}
|
||||
|
||||
void ReadTuplesChunk(ITuple[] into, int firstItemIndex, int countToRead)
|
||||
{
|
||||
_ChunkReader(into, firstItemIndex, countToRead);
|
||||
}
|
||||
/// <summary>
|
||||
/// Because this is backed by a <see cref="LazyList{T}"/>, the value is either returned (if exists) or created and then returned
|
||||
/// </summary>
|
||||
public override ITuple GetTuple(int index) { return _DataSource[index]; }
|
||||
|
||||
/// <summary><see cref="BufferredDataSource{T}.TryGetCachedValue(int, out T)"/></summary>
|
||||
public bool TryGetCachedTuple(int index, out ITuple tuple) { return _DataSource.TryGetCachedValue(index, out tuple); }
|
||||
|
||||
/// <summary>See <see cref="BufferredDataSource{T}.GetValueUnchecked(int)"/></summary>
|
||||
public ITuple GetExistingTuple(int index) { return _DataSource.GetValueUnchecked(index); }
|
||||
|
||||
protected override bool SortTuplesListIfSupported(IComparer comparerToUse)
|
||||
{
|
||||
// No sorting for now, but can be done for smaller data sets. Will probably be implemented in a future version
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool ReverseTuplesListIfSupported()
|
||||
{
|
||||
// Also no reversing for now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa5c2e3f1c162cf4fb290597ff11f3ec
|
||||
timeCreated: 1563565624
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public interface IColumnInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The column's simple name
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name to actually display, which can be different from <see cref="Name"/>
|
||||
/// </summary>
|
||||
string DisplayName { get; set; }
|
||||
|
||||
TableValueType ValueType { get; }
|
||||
|
||||
/// <summary>Only applicable to columns for which <see cref="ValueType"/> is <see cref="TableValueType.ENUMERATION"/>, in which case it becomes required/ </summary>
|
||||
Type EnumValueType { get; }
|
||||
|
||||
/// <summary>The width, for vertical TableViews. -1 to use the prefab's size</summary>
|
||||
float Size { get; }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb0333f852ffea1418191456f3d9c36e
|
||||
timeCreated: 1563189913
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public interface IColumnState
|
||||
{
|
||||
IColumnInfo Info { get; }
|
||||
TableValueSortType CurrentSortingType { get; set; }
|
||||
bool CurrentlyReadOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to <see cref="IColumnInfo.Size"/> on initialization. Can be changed after.
|
||||
/// </summary>
|
||||
float CurrentSize { get; set; }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 405d849c2add9a845a24b0a4962c572b
|
||||
timeCreated: 1563189913
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>
|
||||
/// non-generic interface to access a <see cref="TableAdapter{TParams, TTupleViewsHolder, THeaderTupleViewsHolder}"/>
|
||||
/// </summary>
|
||||
public interface ITableAdapter : IOSA
|
||||
{
|
||||
TableParams TableParameters { get; }
|
||||
ITableViewOptionsPanel Options { get; }
|
||||
ITupleProvider Tuples { get; }
|
||||
ITableColumns Columns { get; }
|
||||
void RefreshRange(int firstIndex, int count);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a615c6f4d3735da4d9de37275bc872b1
|
||||
timeCreated: 1563617598
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores both the configuration and some view state related info, notably the current sorting of each column
|
||||
/// </summary>
|
||||
public interface ITableColumns
|
||||
{
|
||||
int ColumnsCount { get; }
|
||||
ITuple GetColumnsAsTuple();
|
||||
IColumnState GetColumnState(int index);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b08e9514076fe044a499fd71e89c3d3
|
||||
timeCreated: 1563003860
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// If you want to support clearing values, i.e. setting cells or entire columns to null,
|
||||
/// or the other functionalities exposed by <see cref="ITableViewOptionsPanel"/>, implement this, attach it to a game object and assign it to TableAdapter's params.
|
||||
/// </para>
|
||||
/// The <see cref="IsClearing"/> and <see cref="IsLoading"/> are not mutually-exclusive
|
||||
/// </summary>
|
||||
public interface ITableViewOptionsPanel
|
||||
{
|
||||
bool IsClearing { get; set; }
|
||||
bool IsLoading { get; set; }
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 729782eb43241774c94292fba64f0f0d
|
||||
timeCreated: 1563525110
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public interface ITuple
|
||||
{
|
||||
int Length { get; }
|
||||
object GetValue(int index);
|
||||
void SetValue(int index, object value);
|
||||
void ResetValues(IList newValues, bool cloneList);
|
||||
}
|
||||
|
||||
|
||||
public static class ITupleExt
|
||||
{
|
||||
public static void CopyFrom(this ITuple tuple, IEnumerable list)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var item in list)
|
||||
{
|
||||
tuple.SetValue(i, item);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1344815b8b8bed942bd8835ebfa02585
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>
|
||||
/// This provides tuples (a tuple represents a row in a table)
|
||||
/// </summary>
|
||||
public interface ITupleProvider
|
||||
{
|
||||
int Count { get; }
|
||||
/// <summary>
|
||||
/// Whether the user can clear all values for a column when it clicks it while in <see cref="ITableAdapter.Options"/> has IsClearing true
|
||||
/// </summary>
|
||||
bool ColumnClearingSupported { get; }
|
||||
bool ColumnSortingSupported { get; }
|
||||
ITuple GetTuple(int index);
|
||||
bool ChangeColumnSortType(int columnIndex, TableValueType columnType, TableValueSortType currentSorting, TableValueSortType nextSorting);
|
||||
void SetAllValuesOnColumn(int columnIndex, object sameColumnValueInAllTuples);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c27b10fff9908548bd8b14fb04b5045
|
||||
timeCreated: 1563003860
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 715e89d4186da85438ead81f5a10c0fd
|
||||
folderAsset: yes
|
||||
timeCreated: 1563036279
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
public interface ITableViewFloatingDropdown
|
||||
{
|
||||
event Action Closed;
|
||||
UnityEvent<int> onValueChanged { get; }
|
||||
int value { get; set; }
|
||||
int OptionsCount { get; }
|
||||
void ClearOptions();
|
||||
void AddOptions(List<string> options);
|
||||
void Show();
|
||||
void Hide();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70b3d39086f96604d96d4d086614dd6b
|
||||
timeCreated: 1563651051
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
public static class InputValidators
|
||||
{
|
||||
public delegate char StringValidator(string text, int charIndex, char addedChar);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2047647c06cf33741bd4ccb813e90e9e
|
||||
timeCreated: 1563036279
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
public class TableViewFloatingDropdown : UnityEngine.UI.Dropdown, ITableViewFloatingDropdown
|
||||
{
|
||||
public event Action Closed;
|
||||
public int OptionsCount { get { return options.Count; } }
|
||||
UnityEvent<int> ITableViewFloatingDropdown.onValueChanged { get { return base.onValueChanged; } }
|
||||
|
||||
public new DropdownEvent onValueChanged
|
||||
{
|
||||
get { throw new InvalidOperationException("FloatingDropdown.onValueChanged: Not available for this class"); }
|
||||
set { throw new InvalidOperationException("FloatingDropdown.onValueChanged: Not available for this class"); }
|
||||
}
|
||||
public new void Show() { throw new InvalidOperationException("FloatingDropdown.Show() Not available for this class "); }
|
||||
|
||||
void ITableViewFloatingDropdown.Show() { base.Show(); }
|
||||
|
||||
protected override void DestroyDropdownList(GameObject dropdownList)
|
||||
{
|
||||
base.DestroyDropdownList(dropdownList);
|
||||
|
||||
if (Closed != null)
|
||||
Closed();
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a51d16c508165c045be9b90f00319372
|
||||
timeCreated: 1563190545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
public class TableViewFloatingDropdownController : MonoBehaviour
|
||||
{
|
||||
ITableViewFloatingDropdown _Dropdown;
|
||||
Action<int> _CurrentCallback;
|
||||
int _ValueToReturn = -1;
|
||||
//Vector3 _PosToReset;
|
||||
List<object> _Values = new List<object>();
|
||||
// Excluding the Close option
|
||||
List<string> _ValueNames = new List<string>();
|
||||
|
||||
|
||||
protected void Awake()
|
||||
{
|
||||
(transform as RectTransform).pivot = new Vector2(0f, 1f);
|
||||
_Dropdown = GetComponent(typeof(ITableViewFloatingDropdown)) as ITableViewFloatingDropdown;
|
||||
_Dropdown.Closed += OnDropdownClosed;
|
||||
}
|
||||
|
||||
|
||||
public void InitWithEnum(Type enumType, bool sortByEnumNameInsteadOfValue = false, Action<List<object>> valuesFilter = null)
|
||||
{
|
||||
if (enumType == null || !enumType.IsEnum)
|
||||
{
|
||||
enumType = null;
|
||||
ClearOptionsAndTypes();
|
||||
return;
|
||||
}
|
||||
|
||||
var values = Enum.GetValues(enumType);
|
||||
var list = new List<object>();
|
||||
var names = new List<string>();
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
var value = (int)values.GetValue(i);
|
||||
list.Add(value);
|
||||
names.Add(Enum.GetName(enumType, value));
|
||||
}
|
||||
|
||||
if (valuesFilter != null)
|
||||
valuesFilter(list);
|
||||
|
||||
if (sortByEnumNameInsteadOfValue)
|
||||
list.Sort((a, b) => Enum.GetName(enumType, a).CompareTo(Enum.GetName(enumType, b)));
|
||||
else
|
||||
list.Sort();
|
||||
|
||||
InitWithValues(list, names);
|
||||
}
|
||||
|
||||
public void ShowFloating(RectTransform atParent, Action<object> onValueSelected, object invalidValue)
|
||||
{
|
||||
Action<int> onSelected = i =>
|
||||
{
|
||||
if (onValueSelected == null || _Values == null || _Values.Count <= i)
|
||||
return;
|
||||
|
||||
var val = i == -1 ? invalidValue : _Values[i];
|
||||
onValueSelected(val);
|
||||
};
|
||||
|
||||
ShowFloating(atParent, onSelected);
|
||||
}
|
||||
|
||||
public void ClearOptionsAndTypes()
|
||||
{
|
||||
_Values.Clear();
|
||||
_ValueNames.Clear();
|
||||
_Dropdown.ClearOptions();
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
_Dropdown.Hide();
|
||||
}
|
||||
|
||||
void InitWithValues(IList<object> values, IList<string> names)
|
||||
{
|
||||
ClearOptionsAndTypes();
|
||||
_Values.AddRange(values);
|
||||
_ValueNames.AddRange(names);
|
||||
}
|
||||
|
||||
void ShowFloating(RectTransform atParent, Action<int> onSelected)
|
||||
{
|
||||
_CurrentCallback = onSelected;
|
||||
_ValueToReturn = -1;
|
||||
|
||||
_Dropdown.onValueChanged.RemoveListener(OnValueChanged);
|
||||
|
||||
gameObject.SetActive(true);
|
||||
transform.position = atParent.position;
|
||||
|
||||
_Dropdown.ClearOptions();
|
||||
var options = new List<string>(_ValueNames); // modifying a copy
|
||||
|
||||
options.Add("<Close>");
|
||||
_Dropdown.AddOptions(options);
|
||||
_Dropdown.value = options.Count - 1; // selecting an invalid value by default
|
||||
//RefreshShownValue();
|
||||
_Dropdown.Show();
|
||||
|
||||
_Dropdown.onValueChanged.AddListener(OnValueChanged);
|
||||
|
||||
//_PosToReset = transform.localPosition;
|
||||
var asRT = (transform as RectTransform);
|
||||
asRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, atParent.rect.width);
|
||||
asRT.TryClampPositionToParentBoundary();
|
||||
}
|
||||
|
||||
void OnValueChanged(int value)
|
||||
{
|
||||
// The invalid value is returned as -1
|
||||
_ValueToReturn = value == _Dropdown.OptionsCount - 1 ? -1 : value;
|
||||
|
||||
_Dropdown.onValueChanged.RemoveListener(OnValueChanged);
|
||||
_Dropdown.Hide();
|
||||
}
|
||||
|
||||
void OnDropdownClosed()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
if (_CurrentCallback != null)
|
||||
{
|
||||
var callback = _CurrentCallback;
|
||||
_CurrentCallback = null;
|
||||
callback(_ValueToReturn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b8d32910814fa14fa9f66f6d4140c3e
|
||||
timeCreated: 1563649730
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
#if OSA_TV_TMPRO
|
||||
public class TableViewFloatingDropdownTMPro : TMPro.TMP_Dropdown, ITableViewFloatingDropdown
|
||||
{
|
||||
public event Action Closed;
|
||||
public int OptionsCount { get { return options.Count; } }
|
||||
UnityEvent<int> ITableViewFloatingDropdown.onValueChanged { get { return base.onValueChanged; } }
|
||||
|
||||
public new DropdownEvent onValueChanged
|
||||
{
|
||||
get { throw new InvalidOperationException("FloatingDropdown.onValueChanged: Not available for this class"); }
|
||||
set { throw new InvalidOperationException("FloatingDropdown.onValueChanged: Not available for this class"); }
|
||||
}
|
||||
public new void Show() { throw new InvalidOperationException("FloatingDropdown.Show() Not available for this class "); }
|
||||
|
||||
void ITableViewFloatingDropdown.Show() { base.Show(); }
|
||||
|
||||
protected override void DestroyDropdownList(GameObject dropdownList)
|
||||
{
|
||||
base.DestroyDropdownList(dropdownList);
|
||||
|
||||
if (Closed != null)
|
||||
Closed();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6466b4c288bd80845849759bab8c0ec9
|
||||
timeCreated: 1563649730
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
#if OSA_TV_TMPRO
|
||||
using TText = TMPro.TextMeshProUGUI;
|
||||
#else
|
||||
using TText = UnityEngine.UI.Text;
|
||||
#endif
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around a Text or a TMPro.TextMeshProUGUI.
|
||||
/// It expects a Text component attached.
|
||||
/// Or its TMPro counterpart if OSA_TV_TMPRO is defined
|
||||
/// </summary>
|
||||
public class TableViewText : MonoBehaviour
|
||||
{
|
||||
[Tooltip(
|
||||
"Only needed if you want to display something like an ellipsis when the text shown isn't the entire text. " +
|
||||
"If you're using TMPro, you won't need this, as TMPro already has an ellipsis adding mechanism built-in")]
|
||||
[SerializeField]
|
||||
Transform _ObjectToActivateOnOverflow = null;
|
||||
|
||||
/// <summary>
|
||||
/// Keeping the same property name as the Unity's Text component
|
||||
/// </summary>
|
||||
public string text
|
||||
{
|
||||
get { return _Text.text; }
|
||||
set { _Text.text = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeping the same property name as the Unity's Text component
|
||||
/// </summary>
|
||||
public bool supportRichText
|
||||
{
|
||||
#if OSA_TV_TMPRO
|
||||
get { return _Text.richText; }
|
||||
set { _Text.richText = value; }
|
||||
#else
|
||||
get { return _Text.supportRichText; }
|
||||
set { _Text.supportRichText = value; }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeping the same property name as the Unity's Text component
|
||||
/// </summary>
|
||||
public int fontSize
|
||||
{
|
||||
#if OSA_TV_TMPRO
|
||||
get { return (int)(_Text.fontSize + .5f); /*rounding to nearest int*/ }
|
||||
#else
|
||||
get { return _Text.fontSize; }
|
||||
#endif
|
||||
set { _Text.fontSize = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeping the same property name as the Unity's Text component
|
||||
/// </summary>
|
||||
public Color color
|
||||
{
|
||||
get { return _Text.color; }
|
||||
set { _Text.color = value; }
|
||||
}
|
||||
|
||||
public RectTransform RT
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_RetrievedRT)
|
||||
{
|
||||
_RetrievedRT = true;
|
||||
_RT = transform as RectTransform;
|
||||
}
|
||||
|
||||
return _RT;
|
||||
}
|
||||
}
|
||||
|
||||
bool _RetrievedRT;
|
||||
RectTransform _RT;
|
||||
TText _Text;
|
||||
//float _SavedAlpha;
|
||||
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (_Text)
|
||||
_Text.enabled = true;
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_Text = GetComponent<TText>();
|
||||
if (!_Text)
|
||||
throw new OSAException("TableViewText: no " + typeof(TText).Name + " component found (expecting it because OSA_TV_TMPRO scripting symbol is defined)");
|
||||
|
||||
// // The builtin Text will disappear sometimes if verticalOverflow is not set to VerticalWrapMode.Overflow
|
||||
//#if OSA_TV_TMPRO
|
||||
// _Text.overflowMode = TMPro.TextOverflowModes.Ellipsis;
|
||||
//#else
|
||||
// //_Text.verticalOverflow = VerticalWrapMode.Overflow;
|
||||
// if (_ObjectToActivateOnOverflow)
|
||||
// SetOverflowActive(false);
|
||||
//#endif
|
||||
if (_ObjectToActivateOnOverflow)
|
||||
SetOverflowActive(false);
|
||||
}
|
||||
|
||||
// Manually add an Ellipsis if the text overflows, when using the built-in Text component
|
||||
void Update()
|
||||
{
|
||||
// Update at larger intervals, for better performance
|
||||
if (Time.frameCount % 10 == 0)
|
||||
CheckOverflow();
|
||||
}
|
||||
|
||||
void CheckOverflow()
|
||||
{
|
||||
if (!_Text || !_ObjectToActivateOnOverflow)
|
||||
return;
|
||||
|
||||
bool active = false;
|
||||
#if OSA_TV_TMPRO
|
||||
active = _Text.isTextOverflowing;
|
||||
#else
|
||||
var textGen = _Text.cachedTextGenerator;
|
||||
if (textGen != null)
|
||||
active = textGen.characterCountVisible != _Text.text.Length;
|
||||
#endif
|
||||
SetOverflowActive(active);
|
||||
}
|
||||
|
||||
void SetOverflowActive(bool overFlowActive)
|
||||
{
|
||||
//float scaleToSet = overFlowActive ? 1f : 0f;
|
||||
//if (_ObjectToActivateOnOverflow.localScale.x == scaleToSet)
|
||||
// return;
|
||||
|
||||
//var l = _ObjectToActivateOnOverflow.localScale;
|
||||
//l.x = scaleToSet;
|
||||
//_ObjectToActivateOnOverflow.localScale = l;
|
||||
|
||||
_ObjectToActivateOnOverflow.gameObject.SetActive(overFlowActive);
|
||||
}
|
||||
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (_Text)
|
||||
_Text.enabled = false;
|
||||
}
|
||||
|
||||
//public void SetEnabledByScalingGameObject(bool enabled)
|
||||
//{
|
||||
// transform.localScale = enabled ? Vector3.one : Vector3.zero;
|
||||
//}
|
||||
|
||||
/// <summary>Returns the previous alpha</summary>
|
||||
public float SetAlpha(float alpha)
|
||||
{
|
||||
float prevAlpha;
|
||||
if (_Text)
|
||||
{
|
||||
var c = _Text.color;
|
||||
prevAlpha = c.a;
|
||||
c.a = alpha;
|
||||
_Text.color = c;
|
||||
}
|
||||
else
|
||||
prevAlpha = 0f;
|
||||
|
||||
return prevAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2025fbef5c26c1446aaaad9489fe362b
|
||||
timeCreated: 1563447195
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
//#define OSA_TV_TMPRO
|
||||
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using UnityEngine.UI;
|
||||
|
||||
#if OSA_TV_TMPRO
|
||||
using TInputField = TMPro.TMP_InputField;
|
||||
#else
|
||||
using TInputField = UnityEngine.UI.InputField;
|
||||
#endif
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input
|
||||
{
|
||||
/// <summary>
|
||||
/// It expects an InputField in the direct children, and a Text.
|
||||
/// Or their TMPro counterparts if OSA_TV_TMPRO is defined
|
||||
/// </summary>
|
||||
public class TableViewTextInputController : MonoBehaviour
|
||||
{
|
||||
string text
|
||||
{
|
||||
get { return _InputField.text; }
|
||||
set
|
||||
{
|
||||
_InputField.text = value;
|
||||
UpdateSizeControllerText(_InputField.text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeping the same property name as the Unity's Text component
|
||||
/// </summary>
|
||||
public int fontSize
|
||||
{
|
||||
get { return _Text.fontSize; }
|
||||
set
|
||||
{
|
||||
_Text.fontSize = value;
|
||||
_InputField.textComponent.fontSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool interactable { get { return _InputField.interactable; } set { _InputField.interactable = value; } }
|
||||
//public TText textComponent { get { return _InputField.textComponent; } /*set { _InputField.textComponent = value; }*/ }
|
||||
//public Image image { get { return _InputField.image; } /*set { _InputField.image = value; }*/ }
|
||||
|
||||
bool MultiLine
|
||||
{
|
||||
get { return _InputField.multiLine; }
|
||||
set
|
||||
{
|
||||
_InputField.lineType = value ? TInputField.LineType.MultiLineNewline : TInputField.LineType.SingleLine;
|
||||
}
|
||||
}
|
||||
|
||||
//public bool CanAcceptInput { get { return isActiveAndEnabled && interactable && _InputField.isActiveAndEnabled; } }
|
||||
|
||||
RectTransform _RT;
|
||||
TInputField _InputField;
|
||||
TableViewText _Text;
|
||||
LayoutElement _TextLayoutElement;
|
||||
//LayoutElement _MyLayoutElement;
|
||||
Action<string> _CurrentEndEditCallback;
|
||||
Action _CurrentCancelCallback;
|
||||
|
||||
|
||||
//void OnEnable()
|
||||
//{
|
||||
// if (_InputField)
|
||||
// _InputField.enabled = true;
|
||||
// if (_Text)
|
||||
// _Text.enabled = true;
|
||||
//}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_RT = transform as RectTransform;
|
||||
_RT.pivot = new Vector2(0f, 1f); // top-left
|
||||
|
||||
for (int i = 0; i < transform.childCount; i++)
|
||||
{
|
||||
var ch = transform.GetChild(i);
|
||||
if (!_InputField)
|
||||
_InputField = ch.GetComponent<TInputField>();
|
||||
if (!_Text)
|
||||
_Text = ch.GetComponent<TableViewText>();
|
||||
}
|
||||
|
||||
if (!_InputField)
|
||||
throw new OSAException("TableView: no " + typeof(TInputField).Name + " component found in direct children");
|
||||
|
||||
if (!_Text)
|
||||
throw new OSAException("TableView: no " + typeof(TableViewText).Name + " component field found in direct children");
|
||||
|
||||
if (!_InputField.textComponent)
|
||||
throw new OSAException("TableView: the " + typeof(TInputField).Name + " has no text component specified");
|
||||
|
||||
var layEl = _InputField.GetComponent<LayoutElement>();
|
||||
if (!layEl)
|
||||
layEl = _InputField.gameObject.AddComponent<LayoutElement>();
|
||||
|
||||
layEl.ignoreLayout = true;
|
||||
var rt = layEl.transform as RectTransform;
|
||||
rt.MatchParentSize(true);
|
||||
|
||||
_TextLayoutElement = _Text.GetComponent<LayoutElement>();
|
||||
if (!_TextLayoutElement)
|
||||
_TextLayoutElement = _Text.gameObject.AddComponent<LayoutElement>();
|
||||
_TextLayoutElement.preferredHeight = _TextLayoutElement.preferredWidth = -1f;
|
||||
_TextLayoutElement.flexibleHeight = _TextLayoutElement.flexibleWidth = -1f;
|
||||
|
||||
//layEl.flexibleHeight = _FlexibleHeight;
|
||||
//layEl.flexibleWidth = _FlexibleWidth;
|
||||
|
||||
var group = GetComponent<HorizontalLayoutGroup>();
|
||||
if (!group)
|
||||
group = gameObject.AddComponent<HorizontalLayoutGroup>();
|
||||
|
||||
group.childForceExpandHeight = group.childForceExpandWidth = false;
|
||||
group.childControlHeight = group.childControlWidth = true;
|
||||
|
||||
//_MyLayoutElement = GetComponent<LayoutElement>();
|
||||
//if (!_MyLayoutElement)
|
||||
// _MyLayoutElement = gameObject.AddComponent<LayoutElement>();
|
||||
|
||||
var csf = GetComponent<ContentSizeFitter>();
|
||||
if (!csf)
|
||||
csf = gameObject.AddComponent<ContentSizeFitter>();
|
||||
csf.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
_InputField.onValueChanged.AddListener(UpdateSizeControllerText);
|
||||
_InputField.onEndEdit.AddListener(OnEndEdit);
|
||||
}
|
||||
|
||||
//void OnDisable()
|
||||
//{
|
||||
// if (_InputField)
|
||||
// _InputField.enabled = false;
|
||||
// if (_Text)
|
||||
// _Text.enabled = false;
|
||||
//}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (_InputField)
|
||||
_InputField.onValueChanged.RemoveListener(UpdateSizeControllerText);
|
||||
}
|
||||
|
||||
public void ShowFloating(RectTransform atParent, string initialText, bool multiLine, Action<string> onEndEdit, Action onCancel)
|
||||
{
|
||||
_CurrentEndEditCallback = null;
|
||||
_CurrentCancelCallback = null;
|
||||
gameObject.SetActive(true);
|
||||
_CurrentEndEditCallback = onEndEdit;
|
||||
_CurrentCancelCallback = onCancel;
|
||||
|
||||
var parRect = atParent.rect;
|
||||
|
||||
_RT.position = atParent.position;
|
||||
|
||||
_TextLayoutElement.minWidth = _TextLayoutElement.preferredWidth = parRect.width;
|
||||
_TextLayoutElement.minHeight = _TextLayoutElement.preferredHeight = parRect.height;
|
||||
//_RT.SetSizeFromParentEdgeWithCurrentAnchors(_RT.parent as RectTransform, RectTransform.Edge.Left, parRect.width);
|
||||
_RT.TryClampPositionToParentBoundary();
|
||||
|
||||
MultiLine = multiLine;
|
||||
|
||||
ActivateInputField();
|
||||
|
||||
text = initialText;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
_CurrentEndEditCallback = null;
|
||||
|
||||
|
||||
var cancelCallback = _CurrentCancelCallback;
|
||||
bool callCancel = false;
|
||||
_CurrentCancelCallback = null;
|
||||
if (_InputField && _InputField.isActiveAndEnabled && _InputField.isFocused)
|
||||
{
|
||||
DeactivateInputField();
|
||||
callCancel = true;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
|
||||
if (callCancel && cancelCallback != null)
|
||||
cancelCallback();
|
||||
}
|
||||
|
||||
|
||||
void ActivateInputField()
|
||||
{
|
||||
_InputField.ActivateInputField();
|
||||
}
|
||||
void DeactivateInputField()
|
||||
{
|
||||
_InputField.DeactivateInputField();
|
||||
}
|
||||
|
||||
void UpdateSizeControllerText(string _)
|
||||
{
|
||||
_Text.text = _InputField.text;
|
||||
}
|
||||
|
||||
void OnEndEdit(string text)
|
||||
{
|
||||
var c = _CurrentEndEditCallback;
|
||||
|
||||
Hide();
|
||||
|
||||
if (c != null)
|
||||
c(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cd1979504c51a44abb45d3367e45ae6
|
||||
timeCreated: 1563190545
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1049
File diff suppressed because it is too large
Load Diff
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3443d754cee74af49bc0e8304ffcffd7
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>
|
||||
/// A convenience base class for a table's data used to store information for both the columns and tuples (aka 'rows').
|
||||
/// Column sorting is not supported if items count exceeds <see cref="TableViewConst.MAX_TABLE_ENTRIES_FOR_ACCEPTABLE_COLUMN_ITERATION_TIME"/>
|
||||
/// </summary>
|
||||
public abstract class TableData : ITupleProvider
|
||||
{
|
||||
public ITableColumns Columns { get; protected set; }
|
||||
public abstract int Count { get; }
|
||||
public abstract bool ColumnClearingSupported { get; }
|
||||
public bool ColumnSortingSupported { get { return _ColumnSortingSupported; } protected set { _ColumnSortingSupported = value; } }
|
||||
|
||||
bool _ColumnSortingSupported;
|
||||
Dictionary<TableValueType, IComparer> _MapValueTypeToComparer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <paramref name="rowTuples"/> is the list of all the rows in the table, as tuples implementing <see cref="ITuple"/>
|
||||
/// </summary>
|
||||
public TableData(ITableColumns columnsProvider, bool columnSortingSupported)
|
||||
{
|
||||
Init(columnsProvider, _ColumnSortingSupported);
|
||||
}
|
||||
|
||||
protected TableData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>Initialization method provided for inheritors, if needed</summary>
|
||||
protected void Init(ITableColumns columns, bool columnSortingSupported)
|
||||
{
|
||||
Columns = columns;
|
||||
|
||||
int maxCountForSorting = TableViewConst.MAX_TABLE_ENTRIES_FOR_ACCEPTABLE_COLUMN_ITERATION_TIME;
|
||||
if (Count > maxCountForSorting)
|
||||
{
|
||||
if (columnSortingSupported)
|
||||
{
|
||||
Debug.Log(typeof(TableData).Name +
|
||||
": columnSortingSupported is true, but the count exceeds MAX_TABLE_ENTRIES_FOR_ACCEPTABLE_COLUMN_SORTING_TIME " +
|
||||
"(" + Count + " > " + maxCountForSorting + "). Setting columnSortingSupported=false");
|
||||
columnSortingSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
_ColumnSortingSupported = columnSortingSupported;
|
||||
|
||||
if (_ColumnSortingSupported)
|
||||
{
|
||||
_MapValueTypeToComparer = new Dictionary<TableValueType, IComparer>(9);
|
||||
var rawComparerForNull = new ObjectComparerForNull();
|
||||
_MapValueTypeToComparer[TableValueType.RAW] = rawComparerForNull;
|
||||
_MapValueTypeToComparer[TableValueType.STRING] = StringComparer.OrdinalIgnoreCase;
|
||||
_MapValueTypeToComparer[TableValueType.INT] = Comparer<int>.Default;
|
||||
_MapValueTypeToComparer[TableValueType.LONG_INT] = Comparer<long>.Default;
|
||||
_MapValueTypeToComparer[TableValueType.FLOAT] = Comparer<float>.Default;
|
||||
_MapValueTypeToComparer[TableValueType.DOUBLE] = Comparer<double>.Default;
|
||||
_MapValueTypeToComparer[TableValueType.ENUMERATION] = new EnumComparerSupportingNull();
|
||||
_MapValueTypeToComparer[TableValueType.BOOL] = Comparer<bool>.Default;
|
||||
_MapValueTypeToComparer[TableValueType.TEXTURE] = rawComparerForNull;
|
||||
}
|
||||
}
|
||||
|
||||
#region ITupleProvider
|
||||
public abstract ITuple GetTuple(int index);
|
||||
|
||||
/// <summary>Expensive operation, if the table contains a lot of entries</summary>
|
||||
public bool ChangeColumnSortType(int columnIndex, TableValueType columnType, TableValueSortType currentSorting, TableValueSortType nextSorting)
|
||||
{
|
||||
if (!_ColumnSortingSupported)
|
||||
throw new InvalidOperationException("Cannot sort this table model because it was constructed with columnSortingSupported = false");
|
||||
|
||||
// No comparer means changing sort type is not possible
|
||||
IComparer comparer;
|
||||
if (!_MapValueTypeToComparer.TryGetValue(columnType, out comparer))
|
||||
return false;
|
||||
|
||||
// Sort them
|
||||
if (currentSorting == TableValueSortType.NONE)
|
||||
{
|
||||
bool asc = nextSorting == TableValueSortType.ASCENDING;
|
||||
SortTuplesListIfSupported(new TupleComparerWrapper(comparer, asc, columnIndex));
|
||||
}
|
||||
else
|
||||
// No sorting needed, just reversing the list, which is faster
|
||||
ReverseTuplesListIfSupported();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Expensive operation, if the table contains a lot of entries</summary>
|
||||
public void SetAllValuesOnColumn(int columnIndex, object sameColumnValueInAllTuples)
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
GetTuple(i).SetValue(columnIndex, sameColumnValueInAllTuples);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected abstract bool SortTuplesListIfSupported(IComparer comparerToUse);
|
||||
protected abstract bool ReverseTuplesListIfSupported();
|
||||
|
||||
|
||||
protected class ObjectComparerForNull : IComparer
|
||||
{
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
if (y != null)
|
||||
return -1;
|
||||
}
|
||||
else if (y == null)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected class EnumComparerSupportingNull : ObjectComparerForNull
|
||||
{
|
||||
Comparer<Enum> _SystemComparer = Comparer<Enum>.Default;
|
||||
|
||||
|
||||
public int Compare(Enum x, Enum y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
if (y != null)
|
||||
return -1;
|
||||
|
||||
return 0; // both null => equal
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
return 1;
|
||||
|
||||
return _SystemComparer.Compare(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected class TupleComparerWrapper : IComparer
|
||||
{
|
||||
IComparer _Comparer;
|
||||
readonly int _ColumnIndex;
|
||||
int _Sign;
|
||||
|
||||
|
||||
public TupleComparerWrapper(IComparer comparer, bool asc, int columnIndex)
|
||||
{
|
||||
_Comparer = comparer;
|
||||
_ColumnIndex = columnIndex;
|
||||
_Sign = asc ? 1 : -1;
|
||||
}
|
||||
|
||||
|
||||
int IComparer.Compare(object a, object b)
|
||||
{
|
||||
return _Sign * _Comparer.Compare(
|
||||
(a as ITuple).GetValue(_ColumnIndex),
|
||||
(b as ITuple).GetValue(_ColumnIndex)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddd05b2504d685a4297c4bcc8aa4f12d
|
||||
timeCreated: 1563565624
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
/// <summary>Base class for params to be used with a <see cref="GridAdapter{TParams, TCellVH}"/></summary>
|
||||
[Serializable] // serializable, so it can be shown in inspector
|
||||
public class TableParams : BaseParams
|
||||
{
|
||||
#region Configuration
|
||||
[SerializeField]
|
||||
TableConfig _Table = new TableConfig();
|
||||
public TableConfig Table { get { return _Table; } set { _Table = value; } }
|
||||
#endregion
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void InitIfNeeded(IOSA iAdapter)
|
||||
{
|
||||
base.InitIfNeeded(iAdapter);
|
||||
|
||||
if (optimization.ScaleToZeroInsteadOfDisable)
|
||||
{
|
||||
Debug.Log(typeof(TableParams).Name + ": optimization.ScaleToZeroInsteadOfDisable is true, but this is not supported with a TableView. Setting back to false...");
|
||||
optimization.ScaleToZeroInsteadOfDisable = false;
|
||||
}
|
||||
|
||||
if (Navigation.Enabled)
|
||||
{
|
||||
Debug.Log(typeof(TableParams).Name + ": Navigation.Enabled is true, but this is not yet supported with a TableView. Setting back to false...");
|
||||
Navigation.Enabled = false;
|
||||
}
|
||||
|
||||
Table.InitIfNeeded(iAdapter);
|
||||
DefaultItemSize = Table.TuplePrefabSize;
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class TableConfig
|
||||
{
|
||||
[SerializeField]
|
||||
RectTransform _TuplePrefab = null;
|
||||
/// <summary>The prefab to use for each tuple (aka row in database)</summary>
|
||||
public RectTransform TuplePrefab { get { return _TuplePrefab; } set { _TuplePrefab = value; } }
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The prefab to use for the columns header. Can be the same as TuplePrefab")]
|
||||
RectTransform _ColumnsTuplePrefab = null;
|
||||
/// <summary>The prefab to use for the columns header. Can be the same as <see cref="TuplePrefab"/></summary>
|
||||
public RectTransform ColumnsTuplePrefab { get { return _ColumnsTuplePrefab; } set { _ColumnsTuplePrefab = value; } }
|
||||
|
||||
[SerializeField]
|
||||
[FormerlySerializedAs("_ColumnsHeaderSize")]
|
||||
[Tooltip("Size (height for vertical ScrollViews, width otherwise) of the header containing the columns. Leave to -1 to use the prefab's one")]
|
||||
float _ColumnsTupleSize = -1f;
|
||||
/// <summary>Size (height for vertical ScrollViews, width otherwise) of the header containing the columns. Leave to -1 to use the prefab's one</summary>
|
||||
public float ColumnsTupleSize { get { return _ColumnsTupleSize; } set { _ColumnsTupleSize = value; } }
|
||||
|
||||
[SerializeField]
|
||||
[FormerlySerializedAs("_ColumnsHeaderSpacing")]
|
||||
[Tooltip("Additional space between the header and the actual content")]
|
||||
float _ColumnsTupleSpacing = 0f;
|
||||
/// <summary>Additional space between the header and the actual content</summary>
|
||||
public float ColumnsTupleSpacing { get { return _ColumnsTupleSpacing; } set { _ColumnsTupleSpacing = value; } }
|
||||
|
||||
[SerializeField]
|
||||
Scrollbar _ColumnsScrollbar = null;
|
||||
public Scrollbar ColumnsScrollbar { get { return _ColumnsScrollbar; } set { _ColumnsScrollbar = value; } }
|
||||
|
||||
[Tooltip("A GameObject having a component that implements ITableViewFloatingDropdown")]
|
||||
[SerializeField]
|
||||
RectTransform _FloatingDropdownPrefab = null;
|
||||
/// <summary>A GameObject having a component that implements <see cref="Input.ITableViewFloatingDropdown"/></summary>
|
||||
public RectTransform FloatingDropdownPrefab { get { return _FloatingDropdownPrefab; } set { _FloatingDropdownPrefab = value; } }
|
||||
|
||||
[Tooltip("Used for text input")]
|
||||
[SerializeField]
|
||||
TableViewTextInputController _TextInputControllerPrefab = null;
|
||||
/// <summary>Used for text input. See <see cref="Input.TableViewTextInputController"/></summary>
|
||||
public TableViewTextInputController TextInputControllerPrefab { get { return _TextInputControllerPrefab; } set { _TextInputControllerPrefab = value; } }
|
||||
|
||||
[Tooltip("A GameObject having a component that implements ITableViewOptionsPanel")]
|
||||
[SerializeField]
|
||||
RectTransform _OptionsPanel = null;
|
||||
/// <summary>A GameObject having a component that implements <see cref="ITableViewOptionsPanel"/></summary>
|
||||
public RectTransform OptionsPanel { get { return _OptionsPanel; } set { _OptionsPanel = value; } }
|
||||
|
||||
public float TuplePrefabSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!TuplePrefab)
|
||||
throw new OSAException(typeof(TableParams).Name + ": the TuplePrefab was not set. Please set it through inspector or in code");
|
||||
|
||||
if (_TuplePrefabSize == -1f)
|
||||
_TuplePrefabSize = _IsHorizontal ? TuplePrefab.rect.width : TuplePrefab.rect.height;
|
||||
|
||||
return _TuplePrefabSize;
|
||||
}
|
||||
}
|
||||
|
||||
float _TuplePrefabSize = -1f;
|
||||
bool _IsHorizontal;
|
||||
|
||||
|
||||
public void InitIfNeeded(IOSA iAdapter)
|
||||
{
|
||||
string sceneObjectErrSuffix = " should be non-null, and a scene object that's active in hierarchy (i.e. not directly assigned from project view)";
|
||||
if (TuplePrefab == null || !TuplePrefab.gameObject.activeInHierarchy)
|
||||
throw new OSAException("TuplePrefab" + sceneObjectErrSuffix);
|
||||
if (ColumnsTuplePrefab == null || !ColumnsTuplePrefab.gameObject.activeInHierarchy)
|
||||
throw new OSAException("ColumnsTuplePrefab" + sceneObjectErrSuffix);
|
||||
|
||||
_IsHorizontal = iAdapter.IsHorizontal;
|
||||
if (_ColumnsTupleSize == -1f)
|
||||
_ColumnsTupleSize = _TuplePrefab.rect.size[_IsHorizontal ? 0 : 1];
|
||||
|
||||
var adapterParams = iAdapter.BaseParameters;
|
||||
if (TuplePrefab.parent != adapterParams.ScrollViewRT)
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(TuplePrefab.parent as RectTransform);
|
||||
else
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(TuplePrefab);
|
||||
|
||||
adapterParams.AssertValidWidthHeight(TuplePrefab);
|
||||
|
||||
_TuplePrefabSize = -1f; // so the prefab's size will be recalculated next time is accessed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8021a0e05fa76f44183f1c3f85e3ac7b
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public enum TableResizingMode
|
||||
{
|
||||
NONE,
|
||||
|
||||
AUTO_FIT_TUPLE_CONTENT,
|
||||
|
||||
/// <summary>Not implemented yet</summary>
|
||||
MANUAL_COLUMNS,
|
||||
|
||||
MANUAL_TUPLES,
|
||||
|
||||
/// <summary>Not fully implemented yet. Behaves exactly like <see cref="MANUAL_TUPLES"/></summary>
|
||||
MANUAL_COLUMNS_AND_TUPLES,
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19186eb5a20b198488699d59403cd557
|
||||
timeCreated: 1563262587
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public enum TableValueSortType
|
||||
{
|
||||
/// <summary>
|
||||
/// The default order in the provided data. Once changed, it'll only have one of <see cref="ASCENDING"/> or <see cref="DESCENDING"/>
|
||||
/// </summary>
|
||||
NONE,
|
||||
|
||||
ASCENDING,
|
||||
DESCENDING
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e277c8b98e2bce54a9ed2e7560313fa4
|
||||
timeCreated: 1563003860
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Serialization;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public enum TableValueType
|
||||
{
|
||||
/// <summary>
|
||||
/// Will try to call <see cref="String.ToString"/> on the value
|
||||
/// </summary>
|
||||
RAW,
|
||||
|
||||
STRING,
|
||||
|
||||
INT,
|
||||
|
||||
LONG_INT,
|
||||
|
||||
FLOAT,
|
||||
|
||||
DOUBLE,
|
||||
|
||||
/// <summary>
|
||||
/// Will try to cast the value to an integer, then retrieve its enum value using the <see cref="IColumnInfo.EnumValueType"/>.
|
||||
/// If not successful, the raw integer will be shown
|
||||
/// </summary>
|
||||
ENUMERATION,
|
||||
|
||||
BOOL,
|
||||
|
||||
TEXTURE,
|
||||
// TBA
|
||||
//ARRAY
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e0b31d7576be5d488cc7772275878a4
|
||||
timeCreated: 1562942182
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public static class TableViewConst
|
||||
{
|
||||
public const int MAX_TABLE_ENTRIES_FOR_ACCEPTABLE_COLUMN_ITERATION_TIME = 50 * 1000;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 584ba99a42277a046b65fb4239aaf352
|
||||
timeCreated: 1563565624
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView
|
||||
{
|
||||
public static class TableViewUtil
|
||||
{
|
||||
public static TTuple CreateTupleWithEmptyValues<TTuple>(int length)
|
||||
where TTuple : ITuple, new()
|
||||
{
|
||||
var emptyValues = new List<object>(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
emptyValues.Add(null);
|
||||
|
||||
var t = new TTuple();
|
||||
t.ResetValues(emptyValues, false);
|
||||
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff4f71327f0a33e4fbc011d857c02a5f
|
||||
timeCreated: 1563364766
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03800226adeeeee41b6472d30ca2e24a
|
||||
folderAsset: yes
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db67da17f13a51044a296400e50b83fa
|
||||
folderAsset: yes
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple.Basic
|
||||
{
|
||||
public class BasicHeaderTupleAdapter : TupleAdapter<TupleParams, BasicHeaderValueViewsHolder>
|
||||
{
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59d0576dc4bcf2b4db93b01ab0ad8ef5
|
||||
timeCreated: 1563014573
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple.Basic
|
||||
{
|
||||
/// <summary>
|
||||
/// A views holder for a value inside the header.
|
||||
/// See <see cref="TupleValueViewsHolder"/>
|
||||
/// </summary>
|
||||
public class BasicHeaderValueViewsHolder : TupleValueViewsHolder
|
||||
{
|
||||
RectTransform _ArrowRT;
|
||||
|
||||
|
||||
public override void CollectViews()
|
||||
{
|
||||
base.CollectViews();
|
||||
|
||||
_ArrowRT = root.GetComponentAtPath<RectTransform>("SortArrow");
|
||||
}
|
||||
|
||||
public override void UpdateViews(object value, ITableColumns columnsProvider)
|
||||
{
|
||||
string asStr = (string)value;
|
||||
if (columnsProvider.GetColumnState(ItemIndex).CurrentlyReadOnly)
|
||||
{
|
||||
asStr += "\n<color=#00000030><size=10>Read-only</size></color>";
|
||||
}
|
||||
|
||||
TextComponent.text = asStr;
|
||||
|
||||
var sortType = columnsProvider.GetColumnState(ItemIndex).CurrentSortingType;
|
||||
UpdateArrowFromSortType(sortType);
|
||||
}
|
||||
|
||||
void UpdateArrowFromSortType(TableValueSortType type)
|
||||
{
|
||||
if (!_ArrowRT)
|
||||
return;
|
||||
|
||||
if (_ArrowRT)
|
||||
{
|
||||
bool valid = type != TableValueSortType.NONE;
|
||||
float scale;
|
||||
float zRotation;
|
||||
if (valid)
|
||||
{
|
||||
scale = 1f;
|
||||
zRotation = 90f * (type == TableValueSortType.ASCENDING ? 1f : -1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
scale = .5f;
|
||||
zRotation = 0f;
|
||||
}
|
||||
_ArrowRT.localScale = Vector3.one * scale;
|
||||
var euler = _ArrowRT.localRotation.eulerAngles;
|
||||
euler.z = zRotation;
|
||||
_ArrowRT.localRotation = Quaternion.Euler(euler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 031ad4cc2148ba9459c83d86713dd18f
|
||||
timeCreated: 1563014573
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple.Basic
|
||||
{
|
||||
public class BasicTupleAdapter : TupleAdapter<TupleParams, BasicTupleValueViewsHolder>
|
||||
{
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e25d0e466dcb6f2428f2be388929b642
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple.Basic
|
||||
{
|
||||
/// <summary>
|
||||
/// A views holder for a value inside a row from a database, which casts the value passed to it to a
|
||||
/// string or Texture and binds it to a Text or RawImage component, respectively
|
||||
/// </summary>
|
||||
public class BasicTupleValueViewsHolder : TupleValueViewsHolder
|
||||
{
|
||||
RectTransform _ImagePanel;
|
||||
RawImage _Image;
|
||||
RectTransform _TextPanel;
|
||||
RectTransform _TogglePanel;
|
||||
Toggle _Toggle;
|
||||
bool _ForwardValueChanges = true;
|
||||
RectTransform _InputAvailableDot;
|
||||
//bool _IsCurrentlyReadonly;
|
||||
|
||||
//LayoutElement _ImagePanelLE;
|
||||
//LayoutElement _TextPanelLE;
|
||||
//LayoutElement _TogglePanelLE;
|
||||
|
||||
public override void CollectViews()
|
||||
{
|
||||
base.CollectViews();
|
||||
|
||||
root.GetComponentAtPath("ImagePanel", out _ImagePanel);
|
||||
_ImagePanel.GetComponentAtPath("Image", out _Image);
|
||||
|
||||
root.GetComponentAtPath("TextPanel", out _TextPanel);
|
||||
|
||||
//root.GetComponentAtPath("TogglePanel", out _TogglePanel);
|
||||
//_TogglePanel.GetComponentAtPath("Toggle", out _Toggle);
|
||||
|
||||
root.GetComponentAtPath("Toggle", out _TogglePanel);
|
||||
_Toggle = _TogglePanel.GetComponent<Toggle>();
|
||||
|
||||
root.GetComponentAtPath("InputAvailableDot", out _InputAvailableDot);
|
||||
|
||||
_Toggle.onValueChanged.AddListener(OnToggleValueChanged);
|
||||
|
||||
|
||||
//_ImagePanelLE = _ImagePanel.GetComponent<LayoutElement>();
|
||||
//if (!_ImagePanelLE)
|
||||
// _ImagePanelLE = _ImagePanel.gameObject.AddComponent<LayoutElement>();
|
||||
//_ImagePanelLE.ignoreLayout = true;
|
||||
|
||||
//_TextPanelLE = _TextPanel.GetComponent<LayoutElement>();
|
||||
//if (!_TextPanelLE)
|
||||
// _TextPanelLE = _TextPanel.gameObject.AddComponent<LayoutElement>();
|
||||
//_TextPanelLE.ignoreLayout = true;
|
||||
|
||||
//_TogglePanelLE = _TogglePanel.GetComponent<LayoutElement>();
|
||||
//if (!_TogglePanelLE)
|
||||
// _TogglePanelLE = _TogglePanel.gameObject.AddComponent<LayoutElement>();
|
||||
//_TogglePanelLE.ignoreLayout = true;
|
||||
}
|
||||
|
||||
public override void UpdateViews(object value, ITableColumns columnsProvider)
|
||||
{
|
||||
bool isNull = value == null;
|
||||
var column = columnsProvider.GetColumnState(ItemIndex);
|
||||
if (isNull)
|
||||
{
|
||||
UpdateAsNullText(column);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateViews(value, column);
|
||||
}
|
||||
|
||||
void UpdateAsNullText(IColumnState column)
|
||||
{
|
||||
UpdateAsText("<color=#88444499>NULL</color>", false);
|
||||
SetInputAvailable(!column.CurrentlyReadOnly && IsStringInputType(column.Info.ValueType));
|
||||
}
|
||||
|
||||
void UpdateIntOrLong(string asStr, bool canChangeValue)
|
||||
{
|
||||
bool updatedSuccessfully = UpdateAsText(asStr, canChangeValue);
|
||||
SetInputAvailable(canChangeValue && updatedSuccessfully);
|
||||
}
|
||||
|
||||
void UpdateFloatOrDouble(string asStr, bool canChangeValue)
|
||||
{
|
||||
bool updatedSuccessfully = UpdateAsText(asStr, canChangeValue);
|
||||
SetInputAvailable(canChangeValue && updatedSuccessfully);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expecting value to be non-null
|
||||
/// </summary>
|
||||
void UpdateViews(object value, IColumnState column)
|
||||
{
|
||||
bool? textInputAvailable = null;
|
||||
try
|
||||
{
|
||||
bool canChangeValue = !column.CurrentlyReadOnly;
|
||||
bool updatedSuccessfully;
|
||||
switch (column.Info.ValueType)
|
||||
{
|
||||
case TableValueType.RAW:
|
||||
UpdateAsText("<color=#22552266>" + value.GetType().Name + "</color> " + value.ToString(), false);
|
||||
textInputAvailable = false;
|
||||
break;
|
||||
|
||||
case TableValueType.STRING:
|
||||
updatedSuccessfully = UpdateAsText((string)value, canChangeValue);
|
||||
textInputAvailable = canChangeValue && updatedSuccessfully;
|
||||
break;
|
||||
|
||||
case TableValueType.INT:
|
||||
UpdateIntOrLong(((int)value).ToString(), canChangeValue);
|
||||
break;
|
||||
|
||||
case TableValueType.LONG_INT:
|
||||
UpdateIntOrLong(((long)value).ToString(), canChangeValue);
|
||||
break;
|
||||
|
||||
case TableValueType.FLOAT:
|
||||
float fl = (float)value;
|
||||
UpdateFloatOrDouble(fl.ToString(OSAConst.FLOAT_TO_STRING_CONVERSION_SPECIFIER_PRESERVE_PRECISION), canChangeValue);
|
||||
break;
|
||||
|
||||
case TableValueType.DOUBLE:
|
||||
double db = (double)value;
|
||||
//string text = val.ToString();
|
||||
// Spent like 2 hours to find out C# double doesn't always convert successfully to string by default without losing precision, smh.
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#RFormatString
|
||||
// Furthermore, they have a useless R specifier that is more annoying because it doesn't always work. Probably there for historical reasons.
|
||||
// The odd "G17" should be used for making sure a string that doesn't lose precision is output
|
||||
UpdateFloatOrDouble(db.ToString(OSAConst.DOUBLE_TO_STRING_CONVERSION_SPECIFIER_PRESERVE_PRECISION), canChangeValue);
|
||||
break;
|
||||
|
||||
case TableValueType.ENUMERATION:
|
||||
string textToSet = null;
|
||||
if (column.Info.EnumValueType != null && column.Info.EnumValueType.IsEnum)
|
||||
try { textToSet = Enum.GetName(column.Info.EnumValueType, value); } catch { }
|
||||
bool validEnum = !string.IsNullOrEmpty(textToSet);
|
||||
if (!validEnum)
|
||||
textToSet = value.ToString();
|
||||
|
||||
updatedSuccessfully = UpdateAsText(textToSet, false /*enum text is changed by other means, not direct text editing*/);
|
||||
textInputAvailable = false;
|
||||
break;
|
||||
|
||||
case TableValueType.BOOL:
|
||||
updatedSuccessfully = UpdateAsCheckbox((bool)value, canChangeValue);
|
||||
break;
|
||||
|
||||
case TableValueType.TEXTURE:
|
||||
UpdateAsImage((Texture)value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (textInputAvailable != null)
|
||||
SetInputAvailable(textInputAvailable.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Exception pre-details: " + column.Info.ValueType + ", value " + (value == null ? "NULL" : value));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateAsImage(Texture texture)
|
||||
{
|
||||
if (ActivatePanelOnlyFor(_Image))
|
||||
{
|
||||
_Image.texture = texture;
|
||||
}
|
||||
SetInputAvailable(false);
|
||||
}
|
||||
|
||||
protected bool UpdateAsText(string text, bool editable)
|
||||
{
|
||||
if (TextComponent)
|
||||
TextComponent.supportRichText = !editable;
|
||||
|
||||
if (ActivatePanelOnlyFor(TextComponent))
|
||||
{
|
||||
// Don't forward changes that are done from the model
|
||||
_ForwardValueChanges = false;
|
||||
HasPendingTransversalSizeChanges = TextComponent.text != text;
|
||||
if (HasPendingTransversalSizeChanges)
|
||||
{
|
||||
TextComponent.text = text;
|
||||
}
|
||||
_ForwardValueChanges = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool UpdateAsCheckbox(bool value, bool editable)
|
||||
{
|
||||
if (_Toggle)
|
||||
_Toggle.interactable = editable;
|
||||
|
||||
SetInputAvailable(false);
|
||||
|
||||
if (ActivatePanelOnlyFor(_Toggle))
|
||||
{
|
||||
// Don't forward changes that are done from the model
|
||||
_ForwardValueChanges = false;
|
||||
_Toggle.isOn = value;
|
||||
_ForwardValueChanges = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ActivatePanelOnlyFor(UnityEngine.MonoBehaviour uiElement)
|
||||
{
|
||||
bool result = false;
|
||||
if (_Image)
|
||||
{
|
||||
bool act = _Image == uiElement;
|
||||
_ImagePanel.gameObject.SetActive(act);
|
||||
|
||||
result = result || act;
|
||||
}
|
||||
if (TextComponent)
|
||||
{
|
||||
bool act = TextComponent == uiElement;
|
||||
_TextPanel.gameObject.SetActive(act);
|
||||
|
||||
result = result || act;
|
||||
}
|
||||
if (_Toggle)
|
||||
{
|
||||
bool act = _Toggle == uiElement;
|
||||
_TogglePanel.gameObject.SetActive(act);
|
||||
|
||||
result = result || act;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetInputAvailable(bool available)
|
||||
{
|
||||
//_InputAvailableDot.localScale = available ? Vector3.one : Vector3.zero;
|
||||
_InputAvailableDot.gameObject.SetActive(available);
|
||||
}
|
||||
|
||||
void OnToggleValueChanged(bool newValue)
|
||||
{
|
||||
if (_ForwardValueChanges && _TogglePanel.gameObject.activeSelf) // just a sanity check
|
||||
NotifyValueChangedFromInput(newValue);
|
||||
}
|
||||
|
||||
bool IsStringInputType(TableValueType type)
|
||||
{
|
||||
return type == TableValueType.STRING
|
||||
|| type == TableValueType.INT
|
||||
|| type == TableValueType.LONG_INT
|
||||
|| type == TableValueType.FLOAT
|
||||
|| type == TableValueType.DOUBLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4013a7516d696f45945508fbdc5f77b
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple
|
||||
{
|
||||
public interface ITupleAdapter : IOSA
|
||||
{
|
||||
event Action<TupleValueViewsHolder> ValueClicked;
|
||||
event Action<TupleValueViewsHolder, object> ValueChangedFromInput;
|
||||
|
||||
TupleParams TupleParameters { get; }
|
||||
ITupleAdapterSizeHandler SizeHandler { get; set; }
|
||||
|
||||
void ResetWithTuple(ITuple tupleModel, ITableColumns columnsProvider);
|
||||
void ForceUpdateValueViewsHolderIfVisible(int withItemIndex);
|
||||
void ForceUpdateValueViewsHolder(TupleValueViewsHolder vh);
|
||||
void OnWillBeRecycled(float newSize);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed74f9cf454c5ef4085f35804d8b3654
|
||||
timeCreated: 1562927162
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple
|
||||
{
|
||||
public abstract class TupleAdapter<TParams, TTupleValueViewsHolder> : OSA<TParams, TTupleValueViewsHolder>, ITupleAdapter
|
||||
where TParams : TupleParams, new()
|
||||
where TTupleValueViewsHolder : TupleValueViewsHolder, new()
|
||||
{
|
||||
public event Action<TupleValueViewsHolder> ValueClicked;
|
||||
public event Action<TupleValueViewsHolder, object> ValueChangedFromInput;
|
||||
|
||||
public TupleParams TupleParameters { get { return _Params; } }
|
||||
public ITupleAdapterSizeHandler SizeHandler { get; set; }
|
||||
|
||||
public RectTransform RTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_RectTransform == null)
|
||||
_RectTransform = transform as RectTransform;
|
||||
|
||||
return _RectTransform;
|
||||
}
|
||||
}
|
||||
|
||||
// Can be null (for example, when data is not available and will be provided later)
|
||||
protected ITuple _CurrentTuple;
|
||||
protected ITableColumns _ColumnsProvider;
|
||||
RectTransform _RectTransform;
|
||||
float _MyPrevKnownTransvSize;
|
||||
|
||||
|
||||
public void ResetWithTuple(ITuple tuple, ITableColumns columnsProvider)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Init();
|
||||
|
||||
_CurrentTuple = tuple;
|
||||
_ColumnsProvider = columnsProvider;
|
||||
int columnsCount = _ColumnsProvider.ColumnsCount;
|
||||
|
||||
if (GetItemsCount() == columnsCount)
|
||||
{
|
||||
// Save massive amounts of performance by just updating the existing views holders rather than resetting the view.
|
||||
// Same count means existing items don't need to be enabled/disabled/destroyed, because their position won't change
|
||||
var thisAsITupleAdapter = this as ITupleAdapter;
|
||||
for (int i = 0; i < VisibleItemsCount; i++)
|
||||
{
|
||||
var vh = GetItemViewsHolder(i);
|
||||
thisAsITupleAdapter.ForceUpdateValueViewsHolder(vh);
|
||||
}
|
||||
|
||||
// Force a ComputeVisibility pass, if needed
|
||||
//SetNormalizedPosition(GetNormalizedPosition());
|
||||
}
|
||||
else
|
||||
ResetItems(columnsCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start was overridden so that Init is not called automatically (see base.Start()), because this is done manually in the first call of ResetWithTuple().
|
||||
/// <para>See <see cref="OSA{TParams, TItemViewsHolder}.Start"/></para>
|
||||
/// </summary>
|
||||
protected sealed override void Start()
|
||||
{}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!IsInitialized)
|
||||
return;
|
||||
|
||||
CheckResizing();
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_MyPrevKnownTransvSize = GetMyCurrentTransversalSize();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
protected override void CollectItemsSizes(ItemCountChangeMode changeMode, int count, int indexIfInsertingOrRemoving, ItemsDescriptor itemsDesc)
|
||||
{
|
||||
base.CollectItemsSizes(changeMode, count, indexIfInsertingOrRemoving, itemsDesc);
|
||||
|
||||
if (changeMode == ItemCountChangeMode.REMOVE || count == 0)
|
||||
return;
|
||||
|
||||
int indexOfFirstItemThatWillChangeSize;
|
||||
if (changeMode == ItemCountChangeMode.RESET)
|
||||
indexOfFirstItemThatWillChangeSize = 0;
|
||||
else
|
||||
indexOfFirstItemThatWillChangeSize = indexIfInsertingOrRemoving;
|
||||
|
||||
int end = indexOfFirstItemThatWillChangeSize + count;
|
||||
itemsDesc.BeginChangingItemsSizes(indexOfFirstItemThatWillChangeSize);
|
||||
for (int i = indexOfFirstItemThatWillChangeSize; i < end; i++)
|
||||
{
|
||||
var size = _ColumnsProvider.GetColumnState(i).CurrentSize;
|
||||
bool useDefault = size == -1;
|
||||
if (useDefault)
|
||||
size = Parameters.DefaultItemSize;
|
||||
|
||||
itemsDesc[i] = size;
|
||||
}
|
||||
itemsDesc.EndChangingItemsSizes();
|
||||
}
|
||||
|
||||
protected override TTupleValueViewsHolder CreateViewsHolder(int itemIndex)
|
||||
{
|
||||
var vh = new TTupleValueViewsHolder();
|
||||
vh.Init(_Params.ItemPrefab, _Params.Content, itemIndex);
|
||||
vh.SetClickListener(() => OnValueClicked(vh));
|
||||
vh.SetValueChangedFromInputListener(value => OnValueChangedFromInput(vh, value));
|
||||
|
||||
// Fixing text that disappears because all layout elements and groups are disabled on the prefab when resize mode is none, for optimization purposes
|
||||
if (_Params.ResizingMode == TableResizingMode.NONE)
|
||||
{
|
||||
vh.TextComponent.RT.MatchParentSize(true);
|
||||
}
|
||||
|
||||
return vh;
|
||||
}
|
||||
|
||||
protected override void UpdateViewsHolder(TTupleValueViewsHolder newOrRecycled)
|
||||
{
|
||||
object value;
|
||||
if (_CurrentTuple == null) // data pending
|
||||
value = null;
|
||||
else
|
||||
value = _CurrentTuple.GetValue(newOrRecycled.ItemIndex);
|
||||
newOrRecycled.UpdateViews(value, _ColumnsProvider);
|
||||
}
|
||||
|
||||
protected override void OnBeforeDestroyViewsHolder(TTupleValueViewsHolder vh, bool isActive)
|
||||
{
|
||||
vh.SetValueChangedFromInputListener(null);
|
||||
|
||||
base.OnBeforeDestroyViewsHolder(vh, isActive);
|
||||
}
|
||||
|
||||
protected override void OnBeforeRecycleOrDisableViewsHolder(TTupleValueViewsHolder inRecycleBinOrVisible, int newItemIndex)
|
||||
{
|
||||
base.OnBeforeRecycleOrDisableViewsHolder(inRecycleBinOrVisible, newItemIndex);
|
||||
|
||||
if (_Params.ResizingMode == TableResizingMode.AUTO_FIT_TUPLE_CONTENT)
|
||||
{
|
||||
// Make sure items that will just become visible will be rebuilt shortly
|
||||
if (newItemIndex >= 0)
|
||||
inRecycleBinOrVisible.HasPendingTransversalSizeChanges = true;
|
||||
else
|
||||
inRecycleBinOrVisible.HasPendingTransversalSizeChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnValueClicked(TTupleValueViewsHolder vh)
|
||||
{
|
||||
if (ValueClicked != null)
|
||||
ValueClicked(vh);
|
||||
}
|
||||
|
||||
protected virtual void OnValueChangedFromInput(TTupleValueViewsHolder vh, object newValue)
|
||||
{
|
||||
if (ValueChangedFromInput != null)
|
||||
ValueChangedFromInput(vh, newValue);
|
||||
}
|
||||
|
||||
void ITupleAdapter.ForceUpdateValueViewsHolderIfVisible(int withItemIndex)
|
||||
{
|
||||
var vh = GetItemViewsHolderIfVisible(withItemIndex);
|
||||
if (vh != null)
|
||||
UpdateViewsHolder(vh);
|
||||
}
|
||||
|
||||
void ITupleAdapter.ForceUpdateValueViewsHolder(TupleValueViewsHolder vh)
|
||||
{
|
||||
UpdateViewsHolder(vh as TTupleValueViewsHolder);
|
||||
}
|
||||
|
||||
void ITupleAdapter.OnWillBeRecycled(float newSize)
|
||||
{
|
||||
float transvPad = (float)_InternalState.layoutInfo.transversalPaddingStartPlusEnd;
|
||||
bool autoFitEnabled = _Params.ResizingMode == TableResizingMode.AUTO_FIT_TUPLE_CONTENT;
|
||||
var axis = (RectTransform.Axis)(1 - _InternalState.hor0_vert1);
|
||||
// When this entire tuple will be recycled, reset every vh, visible or in recycle cache
|
||||
for (int i = 0; i < VisibleItemsCount; i++)
|
||||
{
|
||||
var vh = GetItemViewsHolder(i);
|
||||
OnBeforeRecycleOrDisableViewsHolder(vh, -1);
|
||||
if (autoFitEnabled)
|
||||
{
|
||||
float valueItemSize = newSize - transvPad;
|
||||
vh.root.SetSizeFromParentEdgeWithCurrentAnchors(_Params.Content, _InternalState.transvStartEdge, valueItemSize);
|
||||
}
|
||||
}
|
||||
if (autoFitEnabled)
|
||||
{
|
||||
for (int i = 0; i < RecyclableItemsCount; i++)
|
||||
{
|
||||
var vh = _RecyclableItems[i];
|
||||
if (autoFitEnabled)
|
||||
{
|
||||
float valueItemSize = newSize - transvPad;
|
||||
// SetSizeWithCurrentAnchors is more efficient when positioning is not important
|
||||
vh.root.SetSizeWithCurrentAnchors(axis, valueItemSize);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < BufferedRecyclableItemsCount; i++)
|
||||
{
|
||||
var vh = _BufferredRecyclableItems[i];
|
||||
if (autoFitEnabled)
|
||||
{
|
||||
float valueItemSize = newSize - transvPad;
|
||||
// SetSizeWithCurrentAnchors is more efficient when positioning is not important
|
||||
vh.root.SetSizeWithCurrentAnchors(axis, valueItemSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float GetMyCurrentTransversalSize()
|
||||
{
|
||||
return _Params.ScrollViewRT.rect.size[1 - _InternalState.hor0_vert1];
|
||||
}
|
||||
|
||||
// When a children's size exceeds this adapter's size or all
|
||||
// children become smaller than this adapter (meaning the adapter should be shrunk by the parent)
|
||||
void CheckResizing()
|
||||
{
|
||||
float myTransvSize = GetMyCurrentTransversalSize();
|
||||
float biggestSize = myTransvSize;
|
||||
float biggestItemTransvSizePlusTransvPadding = 0f;
|
||||
bool resizeNeeded = false;
|
||||
|
||||
if (myTransvSize != _MyPrevKnownTransvSize)
|
||||
{
|
||||
_MyPrevKnownTransvSize = myTransvSize;
|
||||
resizeNeeded = true;
|
||||
}
|
||||
|
||||
// Check if any item has an even bigger size
|
||||
int indexOfBiggestItem = -1;
|
||||
float transvPaddingStart = (float)_InternalState.layoutInfo.transversalPaddingContentStart;
|
||||
float transvPaddingStartPlusEnd = (float)_InternalState.layoutInfo.transversalPaddingStartPlusEnd;
|
||||
bool foundItemBiggerThanMe = false;
|
||||
for (int i = 0; i < VisibleItemsCount; i++)
|
||||
{
|
||||
var vh = GetItemViewsHolder(i);
|
||||
RebuildVHIfNeeded(vh);
|
||||
|
||||
float transvSize = vh.root.rect.size[1 - _InternalState.hor0_vert1];
|
||||
float transvSizePlusTransvPadding = transvSize + transvPaddingStartPlusEnd;
|
||||
if (transvSizePlusTransvPadding > biggestSize)
|
||||
{
|
||||
biggestSize = transvSizePlusTransvPadding;
|
||||
foundItemBiggerThanMe = true;
|
||||
indexOfBiggestItem = i;
|
||||
}
|
||||
if (transvSizePlusTransvPadding > biggestItemTransvSizePlusTransvPadding)
|
||||
biggestItemTransvSizePlusTransvPadding = transvSizePlusTransvPadding;
|
||||
}
|
||||
|
||||
var vhsSmallerThanBiggestSizeMinusPadding = new List<TTupleValueViewsHolder>();
|
||||
for (int i = 0; i < VisibleItemsCount; i++)
|
||||
{
|
||||
var vh = GetItemViewsHolder(i);
|
||||
float transvSize = vh.root.rect.size[1 - _InternalState.hor0_vert1];
|
||||
float transvSizePlusTransvPadding = transvSize + transvPaddingStartPlusEnd;
|
||||
|
||||
if (transvSizePlusTransvPadding < biggestSize)
|
||||
{
|
||||
//Debug.Log(i + ", " + (biggestSize - transvSizePlusTransvPadding) + ", " + transvPaddingStartPlusEnd);
|
||||
vhsSmallerThanBiggestSizeMinusPadding.Add(vh);
|
||||
//vhsSmallerThanMeSizes.Add(transvSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (foundItemBiggerThanMe)
|
||||
{
|
||||
resizeNeeded = true;
|
||||
}
|
||||
|
||||
float sizeToSet = biggestSize;
|
||||
if (!resizeNeeded)
|
||||
{
|
||||
// All items are smaller and also this adapter's size didn't change => consider shrinking
|
||||
|
||||
if (biggestItemTransvSizePlusTransvPadding > 0f)
|
||||
{
|
||||
// Only if it's a significant drop in size
|
||||
if (myTransvSize - biggestItemTransvSizePlusTransvPadding > 1f)
|
||||
{
|
||||
resizeNeeded = true;
|
||||
sizeToSet = biggestItemTransvSizePlusTransvPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_Params.ResizingMode == TableResizingMode.AUTO_FIT_TUPLE_CONTENT)
|
||||
{
|
||||
float itemsSizeToSet = sizeToSet - transvPaddingStartPlusEnd;
|
||||
// Resize smaller items to fill the empty space
|
||||
for (int i = 0; i < vhsSmallerThanBiggestSizeMinusPadding.Count; i++)
|
||||
{
|
||||
// The biggest item is already sized correctly, is indexOfBiggestItem is not -1
|
||||
if (i == indexOfBiggestItem)
|
||||
continue;
|
||||
|
||||
var vh = vhsSmallerThanBiggestSizeMinusPadding[i];
|
||||
//Debug.Log(i + ": " + indexOfBiggestItem + ", " + itemsSizeToSet + ", vh " + vh.root.rect.height);
|
||||
//_Params.SetPaddingTransvEndToAchieveTansvSizeFor(vh.root, vh.LayoutGroup, itemsSizeToSet);
|
||||
vh.root.SetInsetAndSizeFromParentEdgeWithCurrentAnchors(_InternalState.layoutInfo.transvStartEdge, transvPaddingStart, itemsSizeToSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (resizeNeeded)
|
||||
{
|
||||
//Debug.Log(resizeNeeded);
|
||||
//if (indexOfBiggestItem != -1)
|
||||
// Debug.Log(", 1 " + biggestItemTransvSizePlusTransvPadding + ", b " + myTransvSize + ", c " + biggestSize + ", d " + indexOfBiggestItem, gameObject);
|
||||
if (SizeHandler != null)
|
||||
SizeHandler.RequestChangeTransversalSize(this, sizeToSet);
|
||||
}
|
||||
}
|
||||
|
||||
void RebuildVHIfNeeded(TTupleValueViewsHolder vh)
|
||||
{
|
||||
if (vh.HasPendingTransversalSizeChanges)
|
||||
{
|
||||
if (_Params.ResizingMode == TableResizingMode.AUTO_FIT_TUPLE_CONTENT)
|
||||
{
|
||||
// Only rebuild strings
|
||||
if (_ColumnsProvider.GetColumnState(vh.ItemIndex).Info.ValueType == TableValueType.STRING)
|
||||
{
|
||||
if (vh.CSF)
|
||||
ForceRebuildViewsHolder(vh);
|
||||
}
|
||||
}
|
||||
vh.HasPendingTransversalSizeChanges = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ITupleAdapterSizeHandler
|
||||
{
|
||||
void RequestChangeTransversalSize(ITupleAdapter adapter, double size);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 100d5263147813041bd603352d25aee0
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
using System;
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomParams;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple
|
||||
{
|
||||
[Serializable]
|
||||
public class TupleParams : BaseParamsWithPrefab
|
||||
{
|
||||
[SerializeField]
|
||||
RectTransform _EdgeDragger = null;
|
||||
public RectTransform EdgeDragger { get { return _EdgeDragger; } set { _EdgeDragger = value; } }
|
||||
|
||||
[SerializeField]
|
||||
TableResizingMode _ResizingMode = TableResizingMode.NONE;
|
||||
public TableResizingMode ResizingMode { get { return _ResizingMode; } set { _ResizingMode = value; } }
|
||||
|
||||
|
||||
public override void InitIfNeeded(IOSA iAdapter)
|
||||
{
|
||||
if (ItemPrefab == null || !ItemPrefab.gameObject.activeInHierarchy)
|
||||
throw new OSAException(
|
||||
"A Tuple's value prefab should be non-null, and a scene object (i.e. not directly assigned from project view) active in hierarchy." +
|
||||
"This error may be caused by the Tuple prefab itself not being an object present in scene");
|
||||
|
||||
base.InitIfNeeded(iAdapter);
|
||||
|
||||
var tupleAdapter = iAdapter as ITupleAdapter;
|
||||
if (tupleAdapter == null)
|
||||
throw new OSAException(typeof(TupleParams).Name + ": No script implementing " + typeof(ITupleAdapter).Name + " found on TuplePrefab");
|
||||
bool prefabRebuildNeeded = InitResizingNeeded(tupleAdapter);
|
||||
if (prefabRebuildNeeded)
|
||||
InitItemPrefab();
|
||||
else
|
||||
AssertValidWidthHeight(ItemPrefab);
|
||||
|
||||
if (ContentPadding.left == -1 || ContentPadding.right == -1 || ContentPadding.top == -1 || ContentPadding.bottom == -1)
|
||||
throw new OSAException(typeof(TupleParams).Name + ": A Tuple's ContentPadding isn't allowed to have negative components in any direction. Set them to zero or positive values");
|
||||
|
||||
if (Navigation.Enabled)
|
||||
{
|
||||
Debug.Log(typeof(TupleParams).Name + ": Navigation.Enabled is true, but this is not yet supported with a TupleAdapter. Setting back to false...");
|
||||
Navigation.Enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns if prefab needs to be rebuilt
|
||||
bool InitResizingNeeded(ITupleAdapter tupleAdapter)
|
||||
{
|
||||
var tupleParams = tupleAdapter.TupleParameters;
|
||||
var valuePrefab = tupleParams.ItemPrefab;
|
||||
|
||||
var csf = valuePrefab.GetComponent<ContentSizeFitter>();
|
||||
var layoutGroup = valuePrefab.GetComponent<LayoutGroup>();
|
||||
if (ResizingMode == TableResizingMode.AUTO_FIT_TUPLE_CONTENT)
|
||||
{
|
||||
if (EdgeDragger && EdgeDragger.gameObject.activeSelf)
|
||||
EdgeDragger.gameObject.SetActive(false);
|
||||
|
||||
if (tupleParams.ItemTransversalSize != -1f)
|
||||
{
|
||||
Debug.Log(
|
||||
typeof(TupleParams).Name + ": ItemTransversalSize needs to be -1, " +
|
||||
"because ResizingMode is " + ResizingMode +
|
||||
". To avoid this message, manually set it in inspector"
|
||||
);
|
||||
tupleParams.ItemTransversalSize = -1f;
|
||||
}
|
||||
|
||||
if (!csf)
|
||||
{
|
||||
//Debug.Log(
|
||||
// typeof(TupleParams).Name + ": ResizingMode is " + TableResizingMode.AUTO_FIT_TUPLE_CONTENT +
|
||||
// ", but no ContentSizeFitter found on tuple's value prefab. Adding one..."
|
||||
//);
|
||||
csf = valuePrefab.gameObject.AddComponent<ContentSizeFitter>();
|
||||
}
|
||||
var prefr = ContentSizeFitter.FitMode.PreferredSize;
|
||||
var unconstr = ContentSizeFitter.FitMode.Unconstrained;
|
||||
csf.horizontalFit = tupleAdapter.IsHorizontal ? unconstr : prefr;
|
||||
csf.verticalFit = tupleAdapter.IsHorizontal ? prefr : unconstr;
|
||||
|
||||
// Update: will be manually enabled when needed;
|
||||
//csf.enabled = true;
|
||||
csf.enabled = false;
|
||||
|
||||
//var valPrefabLayG = valuePrefab.GetComponent<LayoutGroup>();
|
||||
//HorizontalLayoutGroup valPrefabLayGHor;
|
||||
//if (valPrefabLayG)
|
||||
//{
|
||||
// valPrefabLayGHor = valPrefabLayG as HorizontalLayoutGroup;
|
||||
// if (!valPrefabLayGHor)
|
||||
// throw new OSAException(
|
||||
// typeof(TupleParams).Name + ": Only " + typeof(HorizontalLayoutGroup).Name +
|
||||
// " is allowed on value prefab ATM when using ResizingMode " + TableResizingMode.AUTO_FIT_TUPLE_CONTENT
|
||||
// );
|
||||
//}
|
||||
//else
|
||||
// valPrefabLayGHor = valuePrefab.gameObject.AddComponent<HorizontalLayoutGroup>();
|
||||
|
||||
//if (IsHorizontal)
|
||||
// _PrefabStandardPaddingTransvEnd = valPrefabLayGHor.padding.bottom;
|
||||
//else
|
||||
// _PrefabStandardPaddingTransvEnd = valPrefabLayGHor.padding.right;
|
||||
|
||||
//valPrefabLayG.childAlignment = TextAnchor.UpperLeft;
|
||||
//valPrefabLayGHor.childControlHeight = valPrefabLayGHor.childControlWidth = true;
|
||||
//valPrefabLayGHor.childForceExpandHeight = valPrefabLayGHor.childForceExpandWidth = false;
|
||||
|
||||
//// Make sure the value prefab has the same size as the tuple
|
||||
//SetPaddingTransvEndToAchieveTansvSizeFor(valuePrefab, valPrefabLayG, ScrollViewRT.rect.size[IsHorizontal ? 1 : 0]);
|
||||
|
||||
//return true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EdgeDragger && !EdgeDragger.gameObject.activeSelf)
|
||||
EdgeDragger.gameObject.SetActive(true);
|
||||
|
||||
if (tupleParams.ItemTransversalSize == -1f)
|
||||
{
|
||||
Debug.Log(
|
||||
typeof(TupleParams).Name + ": ItemTransversalSize set to -1 is not supported, " +
|
||||
"because ResizingMode is not " + TableResizingMode.AUTO_FIT_TUPLE_CONTENT +
|
||||
". To avoid this message, manually set it to something else in inspector"
|
||||
);
|
||||
tupleParams.ItemTransversalSize = 0f;
|
||||
}
|
||||
|
||||
if (csf && csf.enabled)
|
||||
{
|
||||
Debug.Log(typeof(TupleParams).Name + ": Found enabled ContentSizeFitter on tuple's value prefab, but ResizingMode is not "
|
||||
+ TableResizingMode.AUTO_FIT_TUPLE_CONTENT + ". Disabling ContentSizeFitter...");
|
||||
csf.enabled = false;
|
||||
}
|
||||
|
||||
if (ResizingMode == TableResizingMode.NONE)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Layout components are disabled when no resizing is available. You
|
||||
// should rely on anchoring to properly size the views in this case
|
||||
if (ResizingMode == TableResizingMode.NONE)
|
||||
{
|
||||
if (layoutGroup && layoutGroup.enabled)
|
||||
{
|
||||
layoutGroup.enabled = false;
|
||||
Debug.Log(typeof(TupleParams).Name + ": Found enabled LayoutGroup on tuple's value prefab, but ResizingMode is " + ResizingMode +
|
||||
". Disabling LayoutGroup...");
|
||||
}
|
||||
|
||||
foreach (RectTransform rt in valuePrefab)
|
||||
{
|
||||
var l = rt.GetComponent<LayoutGroup>();
|
||||
if (l)
|
||||
l.enabled = false;
|
||||
|
||||
var le = rt.GetComponent<LayoutElement>();
|
||||
if (le)
|
||||
le.enabled = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (layoutGroup && !layoutGroup.enabled)
|
||||
layoutGroup.enabled = true;
|
||||
|
||||
foreach (RectTransform rt in valuePrefab)
|
||||
{
|
||||
var l = rt.GetComponent<LayoutGroup>();
|
||||
if (l)
|
||||
l.enabled = true;
|
||||
|
||||
var le = rt.GetComponent<LayoutElement>();
|
||||
if (le)
|
||||
le.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//public void SetPaddingTransvEndToAchieveTansvSizeFor(RectTransform rt, LayoutGroup layoutGroup, double targetTransvSize)
|
||||
//{
|
||||
// double vhTransvSize = rt.rect.size[IsHorizontal ? 1 : 0];
|
||||
// double vhTransvSizeMinusCurrentPaddingBottom = vhTransvSize - layoutGroup.padding.bottom;
|
||||
// double padTransvEnd = targetTransvSize - vhTransvSizeMinusCurrentPaddingBottom;
|
||||
// //if (padTransvEnd < 0d)
|
||||
// // throw new Exception(targetTransvSize + ", " + vhTransvSizeMinusCurrentPaddingBottom);
|
||||
// if (padTransvEnd < _PrefabStandardPaddingTransvEnd)
|
||||
// padTransvEnd = _PrefabStandardPaddingTransvEnd;
|
||||
|
||||
// SetPaddingTransvEndFor(rt, layoutGroup, padTransvEnd);
|
||||
//}
|
||||
|
||||
//public void SetStandardTransvPaddingFor(RectTransform rt, LayoutGroup layoutGroup)
|
||||
//{
|
||||
// SetPaddingTransvEndFor(rt, layoutGroup, _PrefabStandardPaddingTransvEnd);
|
||||
//}
|
||||
|
||||
//void SetPaddingTransvEndFor(RectTransform rt, LayoutGroup layGroup, double paddingTransvEnd)
|
||||
//{
|
||||
// int padEnd = (int)paddingTransvEnd;
|
||||
// if (IsHorizontal)
|
||||
// layGroup.padding.bottom = padEnd;
|
||||
// else
|
||||
// layGroup.padding.right = padEnd;
|
||||
// LayoutRebuilder.MarkLayoutForRebuild(rt);
|
||||
//}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a445190494cb1e4299f52634fbf1ef8
|
||||
timeCreated: 1562924181
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
using Com.ForbiddenByte.OSA.Core;
|
||||
using Com.ForbiddenByte.OSA.CustomAdapters.TableView.Input;
|
||||
using frame8.Logic.Misc.Other.Extensions;
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Com.ForbiddenByte.OSA.CustomAdapters.TableView.Tuple
|
||||
{
|
||||
public abstract class TupleValueViewsHolder : BaseItemViewsHolder
|
||||
{
|
||||
public bool HasPendingTransversalSizeChanges { get; set; }
|
||||
public ContentSizeFitter CSF { get { return _CSF; } }
|
||||
//public LayoutGroup LayoutGroup { get { return _LayoutGroup; } }
|
||||
public TableViewText TextComponent { get { return _TextComponent; } }
|
||||
|
||||
TableViewText _TextComponent;
|
||||
Button _Button;
|
||||
ContentSizeFitter _CSF;
|
||||
//LayoutGroup _LayoutGroup;
|
||||
UnityAction<object> _ValueChangedFromInput;
|
||||
|
||||
|
||||
public override void CollectViews()
|
||||
{
|
||||
base.CollectViews();
|
||||
|
||||
_Button = root.GetComponent<Button>();
|
||||
_CSF = root.GetComponent<UnityEngine.UI.ContentSizeFitter>();
|
||||
//_LayoutGroup = root.GetComponent<UnityEngine.UI.LayoutGroup>();
|
||||
root.GetComponentAtPath("TextPanel/Text", out _TextComponent);
|
||||
}
|
||||
|
||||
public virtual void SetClickListener(UnityAction action)
|
||||
{
|
||||
if (_Button)
|
||||
{
|
||||
if (action == null)
|
||||
_Button.onClick.RemoveAllListeners();
|
||||
else
|
||||
_Button.onClick.AddListener(action);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetValueChangedFromInputListener(UnityAction<object> action)
|
||||
{
|
||||
_ValueChangedFromInput = action;
|
||||
}
|
||||
|
||||
public abstract void UpdateViews(object value, ITableColumns columnsProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Called by the controller of this Views Holder, when a click is not handled by it and should be processed by this Views Holder itself
|
||||
/// </summary>
|
||||
public virtual void ProcessUnhandledClick()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void MarkForRebuild()
|
||||
{
|
||||
// Don't LayoutRebuilder.MarkLayoutForRebuild(), because the tuples in a TableView are rebuilt
|
||||
// via LayoutRebuilder.ForceRebuildLayoutImmediate() by the TupleAdapter itself
|
||||
//base.MarkForRebuild();
|
||||
|
||||
if (CSF)
|
||||
CSF.enabled = true;
|
||||
}
|
||||
|
||||
public override void UnmarkForRebuild()
|
||||
{
|
||||
if (CSF)
|
||||
CSF.enabled = false;
|
||||
|
||||
base.UnmarkForRebuild();
|
||||
}
|
||||
|
||||
protected void NotifyValueChangedFromInput(object newValue)
|
||||
{
|
||||
if (_ValueChangedFromInput != null)
|
||||
_ValueChangedFromInput(newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user