DatePickerContent.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using UnityEngine;
  7. using UnityEngine.Events;
  8. using UnityEngine.Serialization;
  9. using UnityEngine.UI;
  10. namespace Bitsplash.DatePicker
  11. {
  12. [ExecuteInEditMode]
  13. public partial class DatePickerContent : MonoBehaviour , IDatePickerSettingsItem
  14. {
  15. [SerializeField]
  16. [HideInInspector]
  17. private bool isOpen;
  18. const int RowCount = 6;
  19. const int ColumnCount = 7;
  20. [FormerlySerializedAs("FirstDayOfWeek")]
  21. [SerializeField]
  22. [Tooltip("the first day of the week for the content")]
  23. private DayOfWeek firstDayOfWeek = DayOfWeek.Sunday;
  24. [FormerlySerializedAs("RightToLeft")]
  25. [SerializeField]
  26. private bool rightToLeft = false;
  27. [FormerlySerializedAs("BottomToTop")]
  28. [SerializeField]
  29. private bool bottomToTop = false;
  30. [FormerlySerializedAs("CellPrefab")]
  31. [SerializeField]
  32. [Tooltip("drag a cell template here to use it with the datepicker")]
  33. private DatePickerCell cellPrefab = null;
  34. [FormerlySerializedAs("SelectionMode")]
  35. [SerializeField]
  36. [Tooltip("single,range and multiple selection types. ")]
  37. private SelectionType selectionMode;
  38. [FormerlySerializedAs("AllowEmptySelection")]
  39. [SerializeField]
  40. private bool allowEmptySelection = false;
  41. [SerializeField]
  42. private DateTime startDate = new DateTime(1960,1,1);
  43. [SerializeField]
  44. private DateTime endDate = new DateTime(2030, 12, 31);
  45. void ValidateYear()
  46. {
  47. if (endDate < startDate)
  48. endDate = startDate;
  49. mMonthFirst = new DateTime(mMonthFirst.Year, mMonthFirst.Month, 1);
  50. if(mMonthFirst > endDate)
  51. {
  52. mMonthFirst = new DateTime(endDate.Year, endDate.Month, 1);
  53. }
  54. if(mMonthFirst < startDate)
  55. {
  56. mMonthFirst = new DateTime(startDate.Year, startDate.Month, 1);
  57. }
  58. }
  59. public DateTime StartDate
  60. {
  61. get { return startDate; }
  62. set
  63. {
  64. startDate = value.Date;
  65. ValidateYear();
  66. Invalidate();
  67. OnSettingsChanged();
  68. }
  69. }
  70. public DateTime EndDate
  71. {
  72. get { return endDate; }
  73. set
  74. {
  75. endDate = value.Date;
  76. ValidateYear();
  77. Invalidate();
  78. OnSettingsChanged();
  79. }
  80. }
  81. /// <summary>
  82. /// the first day of the week for the date picker
  83. /// </summary>
  84. public DayOfWeek FirstDayOfWeek
  85. {
  86. get { return firstDayOfWeek; }
  87. set
  88. {
  89. firstDayOfWeek = value;
  90. OnSettingsChanged();
  91. }
  92. }
  93. /// <summary>
  94. /// set the date picker to right to left mode
  95. /// </summary>
  96. public bool RightToLeft
  97. {
  98. get { return rightToLeft; }
  99. set
  100. {
  101. rightToLeft = value;
  102. OnSettingsChanged();
  103. }
  104. }
  105. /// <summary>
  106. /// show days from bottom to top instead of top to bottom
  107. /// </summary>
  108. public bool BottomToTop
  109. {
  110. get { return bottomToTop; }
  111. set
  112. {
  113. bottomToTop = value;
  114. OnSettingsChanged();
  115. }
  116. }
  117. //public DatePickerCell CellPrefab
  118. //{
  119. // get { return cellPrefab; }
  120. // set
  121. // {
  122. // cellPrefab = value;
  123. // }
  124. //}
  125. /// <summary>
  126. /// set the selection mode for the date picker. Single ,Range or Multiple
  127. /// </summary>
  128. public SelectionType SelectionMode
  129. {
  130. get { return selectionMode; }
  131. set
  132. {
  133. selectionMode = value;
  134. OnSettingsChanged();
  135. }
  136. }
  137. /// <summary>
  138. /// allows selection of the date picker to be empty
  139. /// </summary>
  140. public bool AllowEmptySelection
  141. {
  142. get { return allowEmptySelection; }
  143. set
  144. {
  145. allowEmptySelection = value;
  146. OnSettingsChanged();
  147. }
  148. }
  149. /// <summary>
  150. /// used for internal purpose
  151. /// </summary>
  152. public event Action SettingsChanged;
  153. /// <summary>
  154. /// currently the displayed month and year
  155. /// </summary>
  156. DateTime mMonthFirst = DateTime.Today;
  157. /// <summary>
  158. /// genearted cells
  159. /// </summary>
  160. DatePickerCell[] mCells;
  161. /// <summary>
  162. /// the selection collection object for the content
  163. /// </summary>
  164. DatePickerCollection mSelection = new DatePickerCollection();
  165. /// <summary>
  166. /// a date to cell map for quick lookup
  167. /// </summary>
  168. Dictionary<DateTime, DatePickerCell> mDateToCell = new Dictionary<DateTime, DatePickerCell>();
  169. /// <summary>
  170. /// an input delegation for the date picker
  171. /// </summary>
  172. DatePickerInput mDatePickerInput;
  173. /// <summary>
  174. /// true if the datepicker should be recreated
  175. /// </summary>
  176. bool mInvalidated = true;
  177. /// <summary>
  178. /// This event triggers when the use navigates the datepicker
  179. /// </summary>
  180. public UnityEvent OnDisplayChanged;
  181. /// <summary>
  182. /// this event triggers when the date selection has changed
  183. /// </summary>
  184. public UnityEvent OnSelectionChanged;
  185. /// <summary>
  186. /// the date picker selection collection. Use this object to change and query the current date selection
  187. /// </summary>
  188. public DatePickerCollection Selection { get { return mSelection; } }
  189. void EnsureInput()
  190. {
  191. mDatePickerInput = GetComponent<DatePickerInput>();
  192. if (mDatePickerInput == null)
  193. mDatePickerInput = gameObject.AddComponent<DatePickerStandardInput>();
  194. }
  195. /// <summary>
  196. /// the currently displayed date in the datepicker
  197. /// </summary>
  198. public DateTime DisplayDate { get { return mMonthFirst; } }
  199. /// <summary>
  200. /// sets the month and year being displayed in the date picker.
  201. /// </summary>
  202. /// <param name="year"></param>
  203. /// <param name="month"></param>
  204. public void SetMonthAndYear(int year,int month)
  205. {
  206. FillCells(new DateTime(year, month, 1));
  207. }
  208. /// <summary>
  209. /// sets the year being displayed in the date picker
  210. /// </summary>
  211. /// <param name="year"></param>
  212. public void SetYear(int year)
  213. {
  214. FillCells(new DateTime(year, mMonthFirst.Month, 1));
  215. }
  216. /// <summary>
  217. /// sets the month being displayed in the date picker
  218. /// </summary>
  219. /// <param name="month"></param>
  220. public void SetMonth(int month)
  221. {
  222. FillCells(new DateTime(mMonthFirst.Year,month, 1));
  223. }
  224. /// <summary>
  225. /// used internally
  226. /// </summary>
  227. public string EditorTitle { get {return "Board"; } }
  228. /// <summary>
  229. /// used internally
  230. /// </summary>
  231. public int Order { get { return 0; } }
  232. /// <summary>
  233. /// advances the display by 1 year
  234. /// </summary>
  235. public void NextYear()
  236. {
  237. FillCells(mMonthFirst.AddYears(1));
  238. }
  239. void OnSettingsChanged()
  240. {
  241. if (SettingsChanged != null)
  242. SettingsChanged();
  243. }
  244. /// <summary>
  245. /// retracts the display by 1 year
  246. /// </summary>
  247. public void PrevYear()
  248. {
  249. FillCells(mMonthFirst.AddYears(-1));
  250. }
  251. /// <summary>
  252. /// advances the display by 1 month
  253. /// </summary>
  254. public void NextMonth()
  255. {
  256. FillCells(mMonthFirst.AddMonths(1));
  257. }
  258. /// <summary>
  259. /// retracts the display by 1 month
  260. /// </summary>
  261. public void PrevMonth()
  262. {
  263. FillCells(mMonthFirst.AddMonths(-1));
  264. }
  265. public virtual string DateToString(DateTime date)
  266. {
  267. return date.Day.ToString();
  268. }
  269. void GenerateCells()
  270. {
  271. Clear();
  272. if (cellPrefab == null)
  273. return;
  274. mCells = new DatePickerCell[((int)RowCount) * ((int)ColumnCount)];
  275. float ColumnSize = 1f / ColumnCount;
  276. float RowSize = 1f / RowCount;
  277. for(float i=0; i<RowCount; i++)
  278. {
  279. float startY = i / RowCount;
  280. if (BottomToTop == false)
  281. startY = 1f - startY - RowSize;
  282. float endY = startY + RowSize;
  283. for (float j=0; j<ColumnCount; j++)
  284. {
  285. float startX = j / ColumnCount;
  286. if (RightToLeft)
  287. startX = 1f - startX - ColumnSize;
  288. float endX = startX + ColumnSize;
  289. GameObject newObj = GameObject.Instantiate(cellPrefab.gameObject, transform);
  290. CommonMethods.SafeDestroy(newObj.GetComponent<DatePickerCellTemplate>());
  291. CommonMethods.HideObject(newObj);
  292. newObj.name = String.Format("day_{0}_{1}", j, i);
  293. newObj.SetActive(true);
  294. CommonMethods.EnsureComponent<IDateTimeItem>(newObj);
  295. var rect = newObj.GetComponent<RectTransform>();
  296. rect.anchorMin = new Vector2(startX, startY);
  297. rect.anchorMax = new Vector2(endX, endY);
  298. rect.anchoredPosition = new Vector2(0f, 0f);
  299. rect.sizeDelta = new Vector2(0f, 0f);
  300. int childIndex = (int)(i * ColumnCount + j);
  301. childIndex = (int)(i * ColumnCount + j);
  302. mCells[childIndex] = newObj.GetComponent<DatePickerCell>();
  303. var addon = CommonMethods.EnsureComponent<CellAddon>(newObj);
  304. addon.SetParent(this, childIndex);
  305. }
  306. }
  307. FillCells(mMonthFirst);
  308. }
  309. DateTime MonthFromDate(DateTime date)
  310. {
  311. return new DateTime(date.Year, date.Month, 1);
  312. }
  313. DatePickerCell getCell(int day,int week)
  314. {
  315. return mCells[week * ColumnCount + day];
  316. }
  317. void FillCells(DateTime monthFirst)
  318. {
  319. monthFirst = monthFirst.Date;
  320. mMonthFirst = monthFirst;
  321. ValidateYear();
  322. if (mCells == null)
  323. return;
  324. monthFirst = mMonthFirst;
  325. int monthDayOfWeek = (int)monthFirst.DayOfWeek;
  326. int span = monthDayOfWeek - (int)FirstDayOfWeek;
  327. if (span < 0)
  328. span += 7;
  329. DateTime startFrom = (monthFirst - TimeSpan.FromDays(span)).Date;
  330. DateTime endIn = startFrom + TimeSpan.FromDays(RowCount * ColumnCount);
  331. DateTime monthLast = monthFirst + TimeSpan.FromDays(DateTime.DaysInMonth(monthFirst.Year, monthFirst.Month) - 1);
  332. DateTime current = startFrom;
  333. mDateToCell.Clear();
  334. for (int i=0; i<mCells.Length; i++)
  335. {
  336. mCells[i].DayValue = current;
  337. mCells[i].SetText(DateToString(current));
  338. bool cellenabled = true;
  339. if (current < monthFirst || current > monthLast || current < startDate || current > endDate)
  340. cellenabled = false;
  341. mCells[i].SetInitialSettings(cellenabled, false);
  342. mDateToCell[current.Date] = mCells[i];
  343. current += TimeSpan.FromDays(1);
  344. }
  345. RefreshSelection();
  346. if (OnDisplayChanged != null)
  347. OnDisplayChanged.Invoke();
  348. }
  349. protected void Clear()
  350. {
  351. IDateTimeItem[] children = GetComponentsInChildren<IDateTimeItem>();
  352. for (int i = 0; i < children.Length; ++i)
  353. {
  354. if (children[i] != null)
  355. {
  356. if (children[i].gameObject.GetComponentInParent<DatePickerContent>() != this)
  357. continue;
  358. if (children[i].gameObject != gameObject)
  359. CommonMethods.SafeDestroy(children[i].gameObject);
  360. }
  361. }
  362. }
  363. public void Invalidate()
  364. {
  365. mInvalidated = true;
  366. }
  367. void HookEvents()
  368. {
  369. ((IDatePickerCollectionPrivate)mSelection).SelectionModified -= DatePicker_SelectionModified;
  370. ((IDatePickerCollectionPrivate)mSelection).SelectionModified += DatePicker_SelectionModified;
  371. }
  372. private void DatePicker_SelectionModified()
  373. {
  374. RaiseSelectionChanged();
  375. }
  376. public void Start()
  377. {
  378. HookEvents();
  379. EnsureInput();
  380. GenerateCells();
  381. // if (AllowEmptySelection == false)
  382. // SelectOne(DateTime.Today);
  383. }
  384. public void Update()
  385. {
  386. if(AllowEmptySelection != ((IDatePickerCollectionPrivate)mSelection).AllowEmpty)
  387. ((IDatePickerCollectionPrivate)mSelection).AllowEmpty = AllowEmptySelection;
  388. UpdateSelection();
  389. if(mInvalidated)
  390. {
  391. GenerateCells();
  392. mInvalidated = false;
  393. }
  394. }
  395. public void OnValidate()
  396. {
  397. ValidateYear();
  398. OnSettingsChanged();
  399. Invalidate();
  400. }
  401. /// <summary>
  402. /// retrives the underlaying gameobject specified by dateTime. If the dateTime is not currently displayed , null is returned
  403. /// </summary>
  404. /// <param name="dateTime"></param>
  405. /// <returns></returns>
  406. public DatePickerCell GetCellObjectByDate(DateTime dateTime)
  407. {
  408. DatePickerCell res = null;
  409. if (mDateToCell.TryGetValue(dateTime.Date, out res))
  410. return res;
  411. return null;
  412. }
  413. }
  414. }