ExpressionEditorWindow.xaml.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. using InABox.Core;
  2. using NPOI.DDF;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using System.Text;
  9. using System.Text.RegularExpressions;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. using System.Windows.Controls;
  13. using System.Windows.Data;
  14. using System.Windows.Documents;
  15. using System.Windows.Input;
  16. using System.Windows.Media;
  17. using System.Windows.Media.Imaging;
  18. using System.Windows.Shapes;
  19. namespace InABox.DynamicGrid
  20. {
  21. /// <summary>
  22. /// Interaction logic for ExpressionEditorWindow.xaml
  23. /// </summary>
  24. public partial class ExpressionEditorWindow : Window
  25. {
  26. public static List<Tuple<string, List<FunctionTemplate>>> FunctionTemplates = new();
  27. public List<string> Variables { get; private set; }
  28. public string Expression
  29. {
  30. get => Editor.Text;
  31. set => Editor.Text = value;
  32. }
  33. private static void RegisterFunctionTemplate(string group, string name, List<string> parameters)
  34. {
  35. var groupList = FunctionTemplates.FirstOrDefault(x => x.Item1 == group)?.Item2;
  36. if(groupList is null)
  37. {
  38. groupList = new List<FunctionTemplate>();
  39. FunctionTemplates.Add(new(group, groupList));
  40. }
  41. groupList.Add(new FunctionTemplate
  42. {
  43. Name = name,
  44. Parameters = parameters.ToList()
  45. });
  46. }
  47. private static void RegisterFunctionTemplate(string group, string name, params string[] parameters)
  48. => RegisterFunctionTemplate(group, name, parameters.ToList());
  49. static ExpressionEditorWindow()
  50. {
  51. RegisterFunctionTemplate("Math", "Abs", "x");
  52. RegisterFunctionTemplate("Math", "Acos", "x");
  53. RegisterFunctionTemplate("Math", "Asin", "x");
  54. RegisterFunctionTemplate("Math", "Atan", "x");
  55. RegisterFunctionTemplate("Math", "Ceiling", "x");
  56. RegisterFunctionTemplate("Math", "Cos", "x");
  57. RegisterFunctionTemplate("Math", "Count", "x");
  58. RegisterFunctionTemplate("Math", "Exp", "exp");
  59. RegisterFunctionTemplate("Math", "Floor", "x");
  60. RegisterFunctionTemplate("Math", "IEEERemainder", "x", "y");
  61. RegisterFunctionTemplate("Math", "Log10", "x");
  62. RegisterFunctionTemplate("Math", "Log", "x", "base");
  63. RegisterFunctionTemplate("Math", "Max", "x", "y");
  64. RegisterFunctionTemplate("Math", "Min", "x", "y");
  65. RegisterFunctionTemplate("Math", "Pow", "base", "exp");
  66. RegisterFunctionTemplate("Math", "Random");
  67. RegisterFunctionTemplate("Math", "Round", "x", "precision");
  68. RegisterFunctionTemplate("Math", "Sign", "x");
  69. RegisterFunctionTemplate("Math", "Sin", "x");
  70. RegisterFunctionTemplate("Math", "Sqrt", "x");
  71. RegisterFunctionTemplate("Math", "Tan", "x");
  72. RegisterFunctionTemplate("Math", "Truncate", "x");
  73. RegisterFunctionTemplate("Logical", "If", "condition", "exp1", "exp2");
  74. RegisterFunctionTemplate("Logical", "In", "value", "...");
  75. RegisterFunctionTemplate("Statistical", "Average", "...");
  76. RegisterFunctionTemplate("Statistical", "Mean", "...");
  77. RegisterFunctionTemplate("Statistical", "Median", "...");
  78. RegisterFunctionTemplate("Statistical", "Mode", "...");
  79. RegisterFunctionTemplate("Statistical", "Sum", "...");
  80. RegisterFunctionTemplate("String", "Contains", "text", "value");
  81. RegisterFunctionTemplate("String", "EndsWith", "text", "value");
  82. RegisterFunctionTemplate("String", "Length", "value");
  83. RegisterFunctionTemplate("String", "PadLeft", "value", "length", "character");
  84. RegisterFunctionTemplate("String", "PadRight", "value", "length", "character");
  85. RegisterFunctionTemplate("String", "StartsWith", "text", "value");
  86. RegisterFunctionTemplate("String", "Substring", "text", "startIndex", "length");
  87. RegisterFunctionTemplate("Date", "AddYears", "date", "years");
  88. RegisterFunctionTemplate("Date", "AddMonths", "date", "months");
  89. RegisterFunctionTemplate("Date", "AddDays", "date", "days");
  90. RegisterFunctionTemplate("Date", "AddHours", "date", "hrs");
  91. RegisterFunctionTemplate("Date", "AddMinutes", "date", "mins");
  92. RegisterFunctionTemplate("Date", "AddSeconds", "date", "secs");
  93. RegisterFunctionTemplate("Date", "AddMilliseconds", "date", "ms");
  94. RegisterFunctionTemplate("Date", "YearOf", "date");
  95. RegisterFunctionTemplate("Date", "MonthOf", "date");
  96. RegisterFunctionTemplate("Date", "DayOf", "date");
  97. RegisterFunctionTemplate("Date", "HourOf", "date");
  98. RegisterFunctionTemplate("Date", "MinuteOf", "date");
  99. RegisterFunctionTemplate("Date", "SecondOf", "date");
  100. RegisterFunctionTemplate("Date", "MillisecondOf", "date");
  101. RegisterFunctionTemplate("Date", "DaysBetween", "date", "date");
  102. RegisterFunctionTemplate("Date", "HoursBetween", "date", "date");
  103. RegisterFunctionTemplate("Date", "MinutesBetween", "date", "date");
  104. RegisterFunctionTemplate("Date", "SecondsBetween", "date", "date");
  105. RegisterFunctionTemplate("Date", "MillisecondsBetween", "date", "date");
  106. RegisterFunctionTemplate("Conversion", "Date", "value");
  107. RegisterFunctionTemplate("Conversion", "Date", "value", "format");
  108. RegisterFunctionTemplate("Conversion", "Decimal", "value");
  109. RegisterFunctionTemplate("Conversion", "Double", "value");
  110. RegisterFunctionTemplate("Conversion", "Integer", "value");
  111. RegisterFunctionTemplate("Conversion", "Long", "value");
  112. RegisterFunctionTemplate("Conversion", "String", "value");
  113. foreach(var function in CoreExpression.Functions)
  114. {
  115. RegisterFunctionTemplate(function.Group, function.Name, function.Parameters);
  116. }
  117. }
  118. public ExpressionEditorWindow(List<string> variables)
  119. {
  120. Variables = variables;
  121. InitializeComponent();
  122. }
  123. private void ExpressionWindow_Loaded(object sender, RoutedEventArgs e)
  124. {
  125. Editor.Focus();
  126. }
  127. private void InsertText(string text)
  128. {
  129. var newCaret = Editor.CaretIndex + text.Length;
  130. Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
  131. Editor.CaretIndex = newCaret;
  132. }
  133. private void AppendText(string text)
  134. {
  135. var oldCaret = Editor.CaretIndex;
  136. Editor.Text = Editor.Text.Insert(Editor.CaretIndex, text);
  137. Editor.CaretIndex = oldCaret;
  138. }
  139. private void DeleteText(int start, int length)
  140. {
  141. var oldCaret = Editor.CaretIndex;
  142. Editor.Text = Editor.Text.Remove(start, length);
  143. if(oldCaret >= start && oldCaret < start + length)
  144. {
  145. Editor.CaretIndex = start;
  146. }
  147. else
  148. {
  149. Editor.CaretIndex = oldCaret - length;
  150. }
  151. }
  152. private static Regex tagRegex = new("\\{\\S+\\}");
  153. private static Regex backTagRegex = new("\\{\\S+\\}", RegexOptions.RightToLeft);
  154. private bool DeletePlaceholder()
  155. {
  156. if (Editor.CaretIndex >= Editor.Text.Length) return false;
  157. var tagStart = Editor.Text.LastIndexOf('{', Editor.CaretIndex);
  158. if (tagStart == -1) return false;
  159. var tagMatch = tagRegex.Match(Editor.Text, tagStart);
  160. if (!tagMatch.Success) return false;
  161. var startIndex = tagMatch.Index;
  162. if (startIndex != tagStart) return false;
  163. var length = tagMatch.Length;
  164. if (Editor.CaretIndex >= startIndex + length) return false;
  165. DeleteText(startIndex, length);
  166. return true;
  167. }
  168. private void JumpPlaceholderBackwards()
  169. {
  170. if (Editor.CaretIndex == 0) return;
  171. var tagMatch = backTagRegex.Match(Editor.Text, Editor.CaretIndex - 1);
  172. if (!tagMatch.Success) return;
  173. Editor.CaretIndex = tagMatch.Index;
  174. }
  175. private void JumpPlaceholder(bool allowStart = false)
  176. {
  177. if (Editor.CaretIndex >= Editor.Text.Length) return;
  178. var start = allowStart ? Editor.CaretIndex : Editor.CaretIndex + 1;
  179. var tagMatch = tagRegex.Match(Editor.Text, start);
  180. if (!tagMatch.Success) return;
  181. Editor.CaretIndex = tagMatch.Index;
  182. }
  183. private void FunctionTemplate_Click(object sender, MouseButtonEventArgs e)
  184. {
  185. if ((sender as FrameworkElement)!.Tag is not FunctionTemplate template) return;
  186. var placeholder = DeletePlaceholder();
  187. InsertText($"{template.Name}(");
  188. AppendText(string.Join(", ", template.Parameters.Select(x => $"{{{x}}}")) + ")");
  189. if (placeholder)
  190. JumpPlaceholder(true);
  191. }
  192. private void Variable_Click(object sender, MouseButtonEventArgs e)
  193. {
  194. if ((sender as FrameworkElement)!.Tag is not string str) return;
  195. DeletePlaceholder();
  196. InsertText($"[{str}]");
  197. }
  198. private void Editor_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
  199. {
  200. e.Handled = true;
  201. }
  202. private void Editor_PreviewTextInput(object sender, TextCompositionEventArgs e)
  203. {
  204. DeletePlaceholder();
  205. }
  206. private void Editor_KeyDown(object sender, KeyEventArgs e)
  207. {
  208. if(e.Key == Key.Tab)
  209. {
  210. if((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
  211. {
  212. JumpPlaceholderBackwards();
  213. }
  214. else
  215. {
  216. JumpPlaceholder();
  217. }
  218. e.Handled = true;
  219. }
  220. }
  221. private void OK_Click(object sender, RoutedEventArgs e)
  222. {
  223. DialogResult = true;
  224. }
  225. }
  226. }