TimeField.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1.  //
  2. // TimeField.cs: text entry for time
  3. //
  4. // Author: Jörg Preiß
  5. //
  6. // Licensed under the MIT license
  7. //
  8. using System;
  9. using System.Globalization;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using NStack;
  13. namespace Terminal.Gui {
  14. /// <summary>
  15. /// Time edit widget
  16. /// </summary>
  17. /// <remarks>
  18. /// This widget provides time editing functionality, and mouse support.
  19. /// </remarks>
  20. public class TimeField : TextField {
  21. bool isShort;
  22. int longFieldLen = 8;
  23. int shortFieldLen = 5;
  24. int FieldLen { get { return isShort ? shortFieldLen : longFieldLen; } }
  25. string sepChar;
  26. string longFormat;
  27. string shortFormat;
  28. string Format { get { return isShort ? shortFormat : longFormat; } }
  29. /// <summary>
  30. /// Public constructor that creates a time edit field at an absolute position and fixed size.
  31. /// </summary>
  32. /// <param name="x">The x coordinate.</param>
  33. /// <param name="y">The y coordinate.</param>
  34. /// <param name="time">Initial time contents.</param>
  35. /// <param name="isShort">If true, the seconds are hidden.</param>
  36. public TimeField (int x, int y, DateTime time, bool isShort = false) : base (x, y, isShort ? 7 : 10, "")
  37. {
  38. CultureInfo cultureInfo = CultureInfo.CurrentCulture;
  39. sepChar = cultureInfo.DateTimeFormat.TimeSeparator;
  40. longFormat = $" HH{sepChar}mm{sepChar}ss";
  41. shortFormat = $" HH{sepChar}mm";
  42. this.isShort = isShort;
  43. CursorPosition = 1;
  44. Time = time;
  45. Changed += TimeField_Changed;
  46. }
  47. void TimeField_Changed (object sender, ustring e)
  48. {
  49. if (!DateTime.TryParseExact (Text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result))
  50. Text = e;
  51. }
  52. /// <summary>
  53. /// Gets or sets the time in the widget.
  54. /// </summary>
  55. /// <remarks>
  56. /// </remarks>
  57. public DateTime Time {
  58. get {
  59. if (!DateTime.TryParseExact (Text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) return new DateTime ();
  60. return result;
  61. }
  62. set {
  63. this.Text = value.ToString (Format);
  64. }
  65. }
  66. bool SetText (Rune key)
  67. {
  68. var text = TextModel.ToRunes (Text);
  69. var newText = text.GetRange (0, CursorPosition);
  70. newText.Add (key);
  71. if (CursorPosition < FieldLen)
  72. newText = newText.Concat (text.GetRange (CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList ();
  73. return SetText (ustring.Make (newText));
  74. }
  75. bool SetText (ustring text)
  76. {
  77. ustring [] vals = text.Split (ustring.Make (sepChar));
  78. bool isValidTime = true;
  79. int hour = Int32.Parse (vals [0].ToString ());
  80. int minute = Int32.Parse (vals [1].ToString ());
  81. int second = isShort ? 0 : Int32.Parse (vals [2].ToString ());
  82. if (hour < 0) {
  83. isValidTime = false;
  84. hour = 0;
  85. vals [0] = "0";
  86. } else if (hour > 23) {
  87. isValidTime = false;
  88. hour = 23;
  89. vals [0] = "23";
  90. }
  91. if (minute < 0) {
  92. isValidTime = false;
  93. minute = 0;
  94. vals [1] = "0";
  95. } else if (minute > 59) {
  96. isValidTime = false;
  97. minute = 59;
  98. vals [1] = "59";
  99. }
  100. if (second < 0) {
  101. isValidTime = false;
  102. second = 0;
  103. vals [2] = "0";
  104. } else if (second > 59) {
  105. isValidTime = false;
  106. second = 59;
  107. vals [2] = "59";
  108. }
  109. string time = isShort ? $" {hour,2:00}{sepChar}{minute,2:00}" : $" {hour,2:00}{sepChar}{minute,2:00}{sepChar}{second,2:00}";
  110. Text = time;
  111. if (!DateTime.TryParseExact (text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result) ||
  112. !isValidTime)
  113. return false;
  114. return true;
  115. }
  116. void IncCursorPosition ()
  117. {
  118. if (CursorPosition == FieldLen)
  119. return;
  120. if (Text [++CursorPosition] == sepChar.ToCharArray () [0])
  121. CursorPosition++;
  122. }
  123. void DecCursorPosition ()
  124. {
  125. if (CursorPosition == 1)
  126. return;
  127. if (Text [--CursorPosition] == sepChar.ToCharArray () [0])
  128. CursorPosition--;
  129. }
  130. void AdjCursorPosition ()
  131. {
  132. if (Text [CursorPosition] == sepChar.ToCharArray () [0])
  133. CursorPosition++;
  134. }
  135. public override bool ProcessKey (KeyEvent kb)
  136. {
  137. switch (kb.Key) {
  138. case Key.DeleteChar:
  139. case Key.ControlD:
  140. SetText ('0');
  141. break;
  142. case Key.Delete:
  143. case Key.Backspace:
  144. SetText ('0');
  145. DecCursorPosition ();
  146. break;
  147. // Home, C-A
  148. case Key.Home:
  149. case Key.ControlA:
  150. CursorPosition = 1;
  151. break;
  152. case Key.CursorLeft:
  153. case Key.ControlB:
  154. DecCursorPosition ();
  155. break;
  156. case Key.End:
  157. case Key.ControlE: // End
  158. CursorPosition = FieldLen;
  159. break;
  160. case Key.CursorRight:
  161. case Key.ControlF:
  162. IncCursorPosition ();
  163. break;
  164. default:
  165. // Ignore non-numeric characters.
  166. if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9'))
  167. return false;
  168. if (SetText (TextModel.ToRunes (ustring.Make ((uint)kb.Key)).First ()))
  169. IncCursorPosition ();
  170. return true;
  171. }
  172. return true;
  173. }
  174. public override bool MouseEvent (MouseEvent ev)
  175. {
  176. if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked))
  177. return false;
  178. if (!HasFocus)
  179. SuperView.SetFocus (this);
  180. var point = ev.X;
  181. if (point > FieldLen)
  182. point = FieldLen;
  183. if (point < 1)
  184. point = 1;
  185. CursorPosition = point;
  186. AdjCursorPosition ();
  187. return true;
  188. }
  189. }
  190. }