You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
9.8KB

  1. #ifndef TSTEXTFIELD_HPP
  2. #define TSTEXTFIELD_HPP
  3. #include "rack.hpp"
  4. #include "widgets.hpp"
  5. using namespace rack;
  6. #include <iostream>
  7. #include <regex>
  8. #include <sstream>
  9. #include <string>
  10. // Integer/Digits: Entire string validation
  11. #define TROWA_REGEX_NUMERIC_STR_ONLY "^[0-9]*$"
  12. // Integer/Digits: Single char validation
  13. #define TROWA_REGEX_NUMERIC_CHAR_ONLY "^[0-9]$"
  14. // Integer/Digits: Not an digit
  15. #define TROWA_REGEX_NUMERIC_CHAR_NOT "[^0-9]"
  16. // Floating Point/Real: Entire string validation -- Format String. The precision should be injected (%d)!!!
  17. //^-?[0-9]+(\\.[0-9]{0,%d})?$
  18. // Now just allow or more decimals
  19. #define TROWA_REGEX_FLOAT_STR_ONLY_FORMAT "^[+-]?([0-9]*[.])?[0-9]+$"//"^-?[0-9]+(\\.[0-9]*)?$"
  20. // Floating Point/Real: Single char validation
  21. #define TROWA_REGEX_FLOAT_CHAR_ONLY "^[\\-0-9\\.]$"
  22. // Floating Point/Real: Not a valid char
  23. #define TROWA_REGEX_FLOAT_CHAR_NOT "[^\\-0-9\\.]"
  24. // IP Address: Entire string validation
  25. #define TROWA_REGEX_IP_ADDRESS "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
  26. // IP Address: Single char validation
  27. #define TROWA_REGEX_IP_CHAR "^([0-9]|\\.)$"
  28. // IP Address: Not a valid character
  29. #define TROWA_REGEX_IP_CHAR_NOT "[^0-9\\.]"
  30. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  31. // isPrintableKey()
  32. // @keyCode : (IN) The key that is pressed.
  33. // @returns: True if the key represents a printable character, false if not.
  34. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  35. bool isPrintableKey(int keyCode);
  36. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  37. // TSTextField - overload rack TextField
  38. // trowaSoft text field with some basic validation and character limiting enforced.
  39. // 1. Consume invalid characters and throw away.
  40. // 2. Validate (regex) input (can be checked with IsValid()).
  41. // 3. Limit input length (with maxLength).
  42. /// TODO: Implement scrolling and cropping so that characters can't break out of the field box.
  43. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  44. /// TODO: Redo this struct completely.
  45. struct TSTextField : TextField {
  46. // Maximum length allowed.
  47. uint16_t maxLength = 50;
  48. // IF text input is enabled.
  49. bool enabled = true;
  50. // The id of this widget. For use like tab order or something.
  51. int id = 0;
  52. // The call back if any (parameter is id). i.e. For tabbing to another field.
  53. // So parent widget should do this since it is aware of any other field siblings,
  54. // or alternatively parent should point to the next guy's requestFocus()....
  55. // EDIT: Realized you can't do function pointer to member procedure in C++, so this may be of limited use.
  56. void (*onTabCallback)(int id) = NULL;
  57. // The call back if any (parameter is id). i.e. For tabbing to the previous field if any.
  58. // So parent widget should do this since it is aware of any other field siblings,
  59. // or alternatively parent should point to the next guy's requestFocus()....
  60. // EDIT: Realized you can't do function pointer to member procedure in C++, so this may be of limited use.
  61. void(*onShiftTabCallback)(int id) = NULL;
  62. // Previous field to focus if Shift-Tab (in lieu of callback).
  63. TSTextField* nextField = NULL;
  64. // Next field to focus if Tab (in lieu of callback).
  65. TSTextField* prevField = NULL;
  66. // If the tab to field is hidden, should we show on tab or go to the next one?
  67. enum TabFieldHiddenAction {
  68. DoNothing,
  69. ShowHiddenTabToField,
  70. MoveToNextVisibleTabField
  71. };
  72. TabFieldHiddenAction tabNextHiddenAction = TabFieldHiddenAction::MoveToNextVisibleTabField;
  73. // Text type for validation of input.
  74. enum TextType {
  75. // Any text allowed (i.e. no regex check).
  76. Any,
  77. // Digits only (no decimals even).
  78. DigitsOnly,
  79. // IP address (digits and periods), IPv4.
  80. IpAddress,
  81. // Real number (or integer), specify precision (max # of decimals).
  82. RealNumberOnly
  83. };
  84. // Text type for validation of input.
  85. TextType allowedTextType = TextType::Any;
  86. // Regex for a single char.
  87. std::regex regexChar;
  88. // Regex for the entire string.
  89. std::regex regexStr;
  90. // Regex for invalid characters (to match on invalid and remove).
  91. std::regex regexInvalidChar;
  92. // The background color.
  93. NVGcolor backgroundColor;
  94. // Font
  95. std::shared_ptr<Font> font;
  96. // Text offset
  97. Vec textOffset;
  98. // Text color
  99. NVGcolor color;
  100. // Font size
  101. float fontSize;
  102. // The caret color - Actually the highlight color.
  103. NVGcolor caretColor;
  104. // The number of decmals allowed.
  105. int realNumberPrecision = 2;
  106. std::string displayStr;
  107. // If this field can be tabbed to.
  108. bool canTabToThisEnabled = true;
  109. int borderWidth = 0;
  110. NVGcolor borderColor;
  111. int getTextPosition(Vec mousePos) override;
  112. TSTextField(TextType textType);
  113. TSTextField(TextType textType, int maxLength);
  114. //-----------------------------------------------------------------------------------------------
  115. // TSTextField()
  116. // @id : (IN) Hopefully unique id for the form. Should indicate tab order.
  117. // @textType: (IN) Text type for this field (for validation).
  118. // @maxLength: (IN) Max length for this field.
  119. // @onTabHandler: (IN) Callback/Event handler for tab (i.e. focus on the next field).
  120. // @onShiftTabHandler: (IN) Callback/Event handler for shift-tab (i.e. focus on the previous field).
  121. //-----------------------------------------------------------------------------------------------
  122. TSTextField(int id, TextType textType, int maxLength, void (*onTabHandler)(int), void (*onShiftTabHandler)(int)) : TSTextField(textType, maxLength) {
  123. this->onTabCallback = onTabHandler;
  124. this->onShiftTabCallback = onShiftTabHandler;
  125. return;
  126. }
  127. // If the text is valid.
  128. bool isValid() {
  129. return (allowedTextType == TextType::Any
  130. || std::regex_match(text, regexStr));
  131. }
  132. // Set the text type/validation
  133. void setTextType(TextType validationType)
  134. {
  135. this->allowedTextType = validationType;
  136. //char buffer[50] = { '\0' };
  137. switch (allowedTextType)
  138. {
  139. case TextType::DigitsOnly:
  140. regexChar = std::regex(TROWA_REGEX_NUMERIC_CHAR_ONLY);
  141. regexStr = std::regex(TROWA_REGEX_NUMERIC_STR_ONLY);
  142. regexInvalidChar = std::regex(TROWA_REGEX_NUMERIC_CHAR_NOT);
  143. break;
  144. case TextType::IpAddress:
  145. regexChar = std::regex(TROWA_REGEX_IP_CHAR);
  146. regexStr = std::regex(TROWA_REGEX_IP_ADDRESS);
  147. regexInvalidChar = std::regex(TROWA_REGEX_IP_CHAR_NOT);
  148. break;
  149. case TextType::RealNumberOnly:
  150. regexChar = std::regex(TROWA_REGEX_FLOAT_CHAR_ONLY);
  151. // Add our number of decimals to the regex:
  152. //sprintf(buffer, TROWA_REGEX_FLOAT_STR_ONLY_FORMAT, this->realNumberPrecision);
  153. //regexStr = std::regex(buffer);
  154. regexStr = std::regex(TROWA_REGEX_FLOAT_STR_ONLY_FORMAT);
  155. regexInvalidChar = std::regex(TROWA_REGEX_FLOAT_CHAR_NOT);
  156. break;
  157. case TextType::Any:
  158. default:
  159. break;
  160. }
  161. return;
  162. }
  163. // Set the decimal precision allowed for the text box.
  164. void setRealPrecision(int precision) {
  165. this->realNumberPrecision = precision;
  166. if (allowedTextType == TextType::RealNumberOnly) {
  167. setTextType(allowedTextType); // Redo our regex.
  168. }
  169. }
  170. // Remove invalid chars.
  171. std::string cleanseString(std::string newText);
  172. void draw(NVGcontext *vg) override;
  173. void insertText(std::string newText);
  174. void onTextChange() override;
  175. void onKey(EventKey &e) override;
  176. // Set the text
  177. void setText(std::string text);
  178. // On key
  179. void onText(EventText &e) override;
  180. // Request focus on this field from the Rack engine.
  181. void requestFocus();
  182. //void onDefocus(EventDefocus &e) override;
  183. // -- TRY TO NOT RESPOND TO EVENTS IF WE ARE HIDING --
  184. /** Called when a mouse button is pressed over this widget
  185. 0 for left, 1 for right, 2 for middle.
  186. Return `this` to accept the event.
  187. Return NULL to reject the event and pass it to the widget behind this one.
  188. */
  189. void onMouseDown(EventMouseDown &e) override {
  190. if (visible) {
  191. TextField::onMouseDown(e);
  192. }
  193. };
  194. void onMouseUp(EventMouseUp &e) override {
  195. if (visible) {
  196. TextField::onMouseUp(e);
  197. }
  198. };
  199. /** Called on every frame, even if mouseRel = Vec(0, 0) */
  200. void onMouseMove(EventMouseMove &e) override {
  201. if (visible) {
  202. TextField::onMouseMove(e);
  203. }
  204. }
  205. void onHoverKey(EventHoverKey &e) override {
  206. if (visible) {
  207. TextField::onHoverKey(e);
  208. }
  209. };
  210. ///** Called when this widget begins responding to `onMouseMove` events */
  211. //virtual void onMouseEnter(EventMouseEnter &e) {}
  212. ///** Called when another widget begins responding to `onMouseMove` events */
  213. //virtual void onMouseLeave(EventMouseLeave &e) {}
  214. //virtual void onFocus(EventFocus &e) {}
  215. //virtual void onDefocus(EventDefocus &e) {}
  216. //virtual void onScroll(EventScroll &e);
  217. /** Called when a widget responds to `onMouseDown` for a left button press */
  218. void onDragStart(EventDragStart &e) override {
  219. if (visible) {
  220. TextField::onDragStart(e);
  221. }
  222. }
  223. /** Called when the left button is released and this widget is being dragged */
  224. void onDragEnd(EventDragEnd &e) override {
  225. if (visible) {
  226. TextField::onDragEnd(e);
  227. }
  228. }
  229. /** Called when a widget responds to `onMouseMove` and is being dragged */
  230. void onDragMove(EventDragMove &e) override {
  231. if (visible) {
  232. TextField::onDragMove(e);
  233. }
  234. }
  235. /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */
  236. void onDragEnter(EventDragEnter &e) override {
  237. if (visible) {
  238. TextField::onDragEnter(e);
  239. }
  240. }
  241. void onDragLeave(EventDragEnter &e) override {
  242. if (visible) {
  243. TextField::onDragLeave(e);
  244. }
  245. }
  246. void onDragDrop(EventDragDrop &e) override {
  247. if (visible) {
  248. TextField::onDragDrop(e);
  249. }
  250. }
  251. void onPathDrop(EventPathDrop &e)override {
  252. if (visible) {
  253. TextField::onPathDrop(e);
  254. }
  255. }
  256. void onAction(EventAction &e) override {
  257. if (visible) {
  258. TextField::onAction(e);
  259. }
  260. }
  261. //void onChange(EventChange &e) override {
  262. //}
  263. }; // end struct TSTextField
  264. #endif // end if not defined