hspylib-clitt 0.9.120__py3-none-any.whl → 0.9.122__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hspylib-clitt might be problematic. Click here for more details.

Files changed (266) hide show
  1. clitt/.version +1 -1
  2. clitt/__init__.py +3 -3
  3. clitt/addons/__init__.py +3 -3
  4. clitt/addons/appman/__init__.py +3 -3
  5. clitt/addons/appman/templates/__init__.py +3 -3
  6. clitt/addons/widman/__init__.py +3 -3
  7. clitt/addons/widman/widgets/__init__.py +3 -3
  8. clitt/core/__init__.py +3 -3
  9. clitt/core/exception/__init__.py +3 -3
  10. clitt/core/icons/__init__.py +3 -3
  11. clitt/core/icons/emojis/__init__.py +3 -3
  12. clitt/core/icons/font_awesome/__init__.py +3 -3
  13. clitt/core/term/__init__.py +3 -3
  14. clitt/core/tui/__init__.py +3 -3
  15. clitt/core/tui/line_input/__init__.py +3 -3
  16. clitt/core/tui/line_input/keyboard_input.py +104 -61
  17. clitt/core/tui/mchoose/__init__.py +3 -3
  18. clitt/core/tui/mdashboard/__init__.py +3 -3
  19. clitt/core/tui/menu/__init__.py +3 -3
  20. clitt/core/tui/minput/__init__.py +3 -3
  21. clitt/core/tui/minput/menu_input.py +7 -15
  22. clitt/core/tui/mselect/__init__.py +3 -3
  23. clitt/core/tui/table/__init__.py +3 -3
  24. clitt/utils/__init__.py +3 -3
  25. {hspylib_clitt-0.9.120.dist-info → hspylib_clitt-0.9.122.dist-info}/METADATA +2 -2
  26. hspylib_clitt-0.9.122.dist-info/RECORD +97 -0
  27. {hspylib_clitt-0.9.120.dist-info → hspylib_clitt-0.9.122.dist-info}/top_level.txt +0 -1
  28. build/lib/build/lib/build/lib/clitt/__classpath__.py +0 -28
  29. build/lib/build/lib/build/lib/clitt/__init__.py +0 -13
  30. build/lib/build/lib/build/lib/clitt/__main__.py +0 -139
  31. build/lib/build/lib/build/lib/clitt/addons/__init__.py +0 -12
  32. build/lib/build/lib/build/lib/clitt/addons/appman/__init__.py +0 -13
  33. build/lib/build/lib/build/lib/clitt/addons/appman/appman.py +0 -305
  34. build/lib/build/lib/build/lib/clitt/addons/appman/appman_enums.py +0 -39
  35. build/lib/build/lib/build/lib/clitt/addons/appman/templates/__init__.py +0 -11
  36. build/lib/build/lib/build/lib/clitt/addons/widman/__init__.py +0 -14
  37. build/lib/build/lib/build/lib/clitt/addons/widman/widget.py +0 -70
  38. build/lib/build/lib/build/lib/clitt/addons/widman/widget_entry.py +0 -54
  39. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/__init__.py +0 -14
  40. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_free.py +0 -110
  41. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_punch.py +0 -246
  42. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_send_msg.py +0 -272
  43. build/lib/build/lib/build/lib/clitt/addons/widman/widgets/widget_time_calc.py +0 -146
  44. build/lib/build/lib/build/lib/clitt/addons/widman/widman.py +0 -123
  45. build/lib/build/lib/build/lib/clitt/core/__init__.py +0 -15
  46. build/lib/build/lib/build/lib/clitt/core/exception/__init__.py +0 -11
  47. build/lib/build/lib/build/lib/clitt/core/exception/exceptions.py +0 -19
  48. build/lib/build/lib/build/lib/clitt/core/icons/__init__.py +0 -12
  49. build/lib/build/lib/build/lib/clitt/core/icons/emojis/__init__.py +0 -12
  50. build/lib/build/lib/build/lib/clitt/core/icons/emojis/emojis.py +0 -41
  51. build/lib/build/lib/build/lib/clitt/core/icons/emojis/face_smiling.py +0 -40
  52. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/__init__.py +0 -18
  53. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/app_icons.py +0 -55
  54. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/awesome.py +0 -76
  55. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/dashboard_icons.py +0 -93
  56. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/form_icons.py +0 -69
  57. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/game_icons.py +0 -59
  58. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/nav_icons.py +0 -42
  59. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/trickplay_icons.py +0 -39
  60. build/lib/build/lib/build/lib/clitt/core/icons/font_awesome/widget_icons.py +0 -37
  61. build/lib/build/lib/build/lib/clitt/core/preferences.py +0 -87
  62. build/lib/build/lib/build/lib/clitt/core/term/__init__.py +0 -14
  63. build/lib/build/lib/build/lib/clitt/core/term/commons.py +0 -106
  64. build/lib/build/lib/build/lib/clitt/core/term/cursor.py +0 -174
  65. build/lib/build/lib/build/lib/clitt/core/term/screen.py +0 -106
  66. build/lib/build/lib/build/lib/clitt/core/term/terminal.py +0 -202
  67. build/lib/build/lib/build/lib/clitt/core/tui/__init__.py +0 -20
  68. build/lib/build/lib/build/lib/clitt/core/tui/line_input/__init__.py +0 -12
  69. build/lib/build/lib/build/lib/clitt/core/tui/line_input/keyboard_input.py +0 -229
  70. build/lib/build/lib/build/lib/clitt/core/tui/line_input/line_input.py +0 -31
  71. build/lib/build/lib/build/lib/clitt/core/tui/mchoose/__init__.py +0 -12
  72. build/lib/build/lib/build/lib/clitt/core/tui/mchoose/mchoose.py +0 -43
  73. build/lib/build/lib/build/lib/clitt/core/tui/mchoose/menu_choose.py +0 -192
  74. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/__init__.py +0 -14
  75. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_builder.py +0 -54
  76. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_item.py +0 -31
  77. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/mdashboard.py +0 -26
  78. build/lib/build/lib/build/lib/clitt/core/tui/mdashboard/menu_dashboard.py +0 -148
  79. build/lib/build/lib/build/lib/clitt/core/tui/menu/__init__.py +0 -16
  80. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu.py +0 -111
  81. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_action.py +0 -47
  82. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_factory.py +0 -117
  83. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_item.py +0 -196
  84. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_ui.py +0 -98
  85. build/lib/build/lib/build/lib/clitt/core/tui/menu/tui_menu_view.py +0 -57
  86. build/lib/build/lib/build/lib/clitt/core/tui/minput/__init__.py +0 -19
  87. build/lib/build/lib/build/lib/clitt/core/tui/minput/access_type.py +0 -26
  88. build/lib/build/lib/build/lib/clitt/core/tui/minput/field_builder.py +0 -117
  89. build/lib/build/lib/build/lib/clitt/core/tui/minput/form_builder.py +0 -72
  90. build/lib/build/lib/build/lib/clitt/core/tui/minput/form_field.py +0 -180
  91. build/lib/build/lib/build/lib/clitt/core/tui/minput/input_type.py +0 -30
  92. build/lib/build/lib/build/lib/clitt/core/tui/minput/input_validator.py +0 -98
  93. build/lib/build/lib/build/lib/clitt/core/tui/minput/menu_input.py +0 -292
  94. build/lib/build/lib/build/lib/clitt/core/tui/minput/minput.py +0 -44
  95. build/lib/build/lib/build/lib/clitt/core/tui/minput/minput_utils.py +0 -156
  96. build/lib/build/lib/build/lib/clitt/core/tui/mselect/__init__.py +0 -12
  97. build/lib/build/lib/build/lib/clitt/core/tui/mselect/menu_select.py +0 -170
  98. build/lib/build/lib/build/lib/clitt/core/tui/mselect/mselect.py +0 -36
  99. build/lib/build/lib/build/lib/clitt/core/tui/table/__init__.py +0 -12
  100. build/lib/build/lib/build/lib/clitt/core/tui/table/table_enums.py +0 -47
  101. build/lib/build/lib/build/lib/clitt/core/tui/table/table_renderer.py +0 -339
  102. build/lib/build/lib/build/lib/clitt/core/tui/tui_application.py +0 -52
  103. build/lib/build/lib/build/lib/clitt/core/tui/tui_component.py +0 -154
  104. build/lib/build/lib/build/lib/clitt/core/tui/tui_preferences.py +0 -103
  105. build/lib/build/lib/build/lib/clitt/utils/__init__.py +0 -11
  106. build/lib/build/lib/build/lib/clitt/utils/git_utils.py +0 -66
  107. build/lib/build/lib/clitt/__classpath__.py +0 -28
  108. build/lib/build/lib/clitt/__init__.py +0 -13
  109. build/lib/build/lib/clitt/__main__.py +0 -139
  110. build/lib/build/lib/clitt/addons/__init__.py +0 -12
  111. build/lib/build/lib/clitt/addons/appman/__init__.py +0 -13
  112. build/lib/build/lib/clitt/addons/appman/appman.py +0 -305
  113. build/lib/build/lib/clitt/addons/appman/appman_enums.py +0 -39
  114. build/lib/build/lib/clitt/addons/appman/templates/__init__.py +0 -11
  115. build/lib/build/lib/clitt/addons/widman/__init__.py +0 -14
  116. build/lib/build/lib/clitt/addons/widman/widget.py +0 -70
  117. build/lib/build/lib/clitt/addons/widman/widget_entry.py +0 -54
  118. build/lib/build/lib/clitt/addons/widman/widgets/__init__.py +0 -14
  119. build/lib/build/lib/clitt/addons/widman/widgets/widget_free.py +0 -110
  120. build/lib/build/lib/clitt/addons/widman/widgets/widget_punch.py +0 -246
  121. build/lib/build/lib/clitt/addons/widman/widgets/widget_send_msg.py +0 -272
  122. build/lib/build/lib/clitt/addons/widman/widgets/widget_time_calc.py +0 -146
  123. build/lib/build/lib/clitt/addons/widman/widman.py +0 -123
  124. build/lib/build/lib/clitt/core/__init__.py +0 -15
  125. build/lib/build/lib/clitt/core/exception/__init__.py +0 -11
  126. build/lib/build/lib/clitt/core/exception/exceptions.py +0 -19
  127. build/lib/build/lib/clitt/core/icons/__init__.py +0 -12
  128. build/lib/build/lib/clitt/core/icons/emojis/__init__.py +0 -12
  129. build/lib/build/lib/clitt/core/icons/emojis/emojis.py +0 -41
  130. build/lib/build/lib/clitt/core/icons/emojis/face_smiling.py +0 -40
  131. build/lib/build/lib/clitt/core/icons/font_awesome/__init__.py +0 -18
  132. build/lib/build/lib/clitt/core/icons/font_awesome/app_icons.py +0 -55
  133. build/lib/build/lib/clitt/core/icons/font_awesome/awesome.py +0 -76
  134. build/lib/build/lib/clitt/core/icons/font_awesome/dashboard_icons.py +0 -93
  135. build/lib/build/lib/clitt/core/icons/font_awesome/form_icons.py +0 -69
  136. build/lib/build/lib/clitt/core/icons/font_awesome/game_icons.py +0 -59
  137. build/lib/build/lib/clitt/core/icons/font_awesome/nav_icons.py +0 -42
  138. build/lib/build/lib/clitt/core/icons/font_awesome/trickplay_icons.py +0 -39
  139. build/lib/build/lib/clitt/core/icons/font_awesome/widget_icons.py +0 -37
  140. build/lib/build/lib/clitt/core/preferences.py +0 -87
  141. build/lib/build/lib/clitt/core/term/__init__.py +0 -14
  142. build/lib/build/lib/clitt/core/term/commons.py +0 -106
  143. build/lib/build/lib/clitt/core/term/cursor.py +0 -174
  144. build/lib/build/lib/clitt/core/term/screen.py +0 -106
  145. build/lib/build/lib/clitt/core/term/terminal.py +0 -202
  146. build/lib/build/lib/clitt/core/tui/__init__.py +0 -20
  147. build/lib/build/lib/clitt/core/tui/line_input/__init__.py +0 -12
  148. build/lib/build/lib/clitt/core/tui/line_input/keyboard_input.py +0 -229
  149. build/lib/build/lib/clitt/core/tui/line_input/line_input.py +0 -31
  150. build/lib/build/lib/clitt/core/tui/mchoose/__init__.py +0 -12
  151. build/lib/build/lib/clitt/core/tui/mchoose/mchoose.py +0 -43
  152. build/lib/build/lib/clitt/core/tui/mchoose/menu_choose.py +0 -192
  153. build/lib/build/lib/clitt/core/tui/mdashboard/__init__.py +0 -14
  154. build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_builder.py +0 -54
  155. build/lib/build/lib/clitt/core/tui/mdashboard/dashboard_item.py +0 -31
  156. build/lib/build/lib/clitt/core/tui/mdashboard/mdashboard.py +0 -26
  157. build/lib/build/lib/clitt/core/tui/mdashboard/menu_dashboard.py +0 -148
  158. build/lib/build/lib/clitt/core/tui/menu/__init__.py +0 -16
  159. build/lib/build/lib/clitt/core/tui/menu/tui_menu.py +0 -111
  160. build/lib/build/lib/clitt/core/tui/menu/tui_menu_action.py +0 -47
  161. build/lib/build/lib/clitt/core/tui/menu/tui_menu_factory.py +0 -117
  162. build/lib/build/lib/clitt/core/tui/menu/tui_menu_item.py +0 -196
  163. build/lib/build/lib/clitt/core/tui/menu/tui_menu_ui.py +0 -98
  164. build/lib/build/lib/clitt/core/tui/menu/tui_menu_view.py +0 -57
  165. build/lib/build/lib/clitt/core/tui/minput/__init__.py +0 -19
  166. build/lib/build/lib/clitt/core/tui/minput/access_type.py +0 -26
  167. build/lib/build/lib/clitt/core/tui/minput/field_builder.py +0 -117
  168. build/lib/build/lib/clitt/core/tui/minput/form_builder.py +0 -72
  169. build/lib/build/lib/clitt/core/tui/minput/form_field.py +0 -180
  170. build/lib/build/lib/clitt/core/tui/minput/input_type.py +0 -30
  171. build/lib/build/lib/clitt/core/tui/minput/input_validator.py +0 -98
  172. build/lib/build/lib/clitt/core/tui/minput/menu_input.py +0 -292
  173. build/lib/build/lib/clitt/core/tui/minput/minput.py +0 -44
  174. build/lib/build/lib/clitt/core/tui/minput/minput_utils.py +0 -156
  175. build/lib/build/lib/clitt/core/tui/mselect/__init__.py +0 -12
  176. build/lib/build/lib/clitt/core/tui/mselect/menu_select.py +0 -170
  177. build/lib/build/lib/clitt/core/tui/mselect/mselect.py +0 -36
  178. build/lib/build/lib/clitt/core/tui/table/__init__.py +0 -12
  179. build/lib/build/lib/clitt/core/tui/table/table_enums.py +0 -47
  180. build/lib/build/lib/clitt/core/tui/table/table_renderer.py +0 -339
  181. build/lib/build/lib/clitt/core/tui/tui_application.py +0 -52
  182. build/lib/build/lib/clitt/core/tui/tui_component.py +0 -154
  183. build/lib/build/lib/clitt/core/tui/tui_preferences.py +0 -103
  184. build/lib/build/lib/clitt/utils/__init__.py +0 -11
  185. build/lib/build/lib/clitt/utils/git_utils.py +0 -66
  186. build/lib/clitt/__classpath__.py +0 -28
  187. build/lib/clitt/__init__.py +0 -13
  188. build/lib/clitt/__main__.py +0 -139
  189. build/lib/clitt/addons/__init__.py +0 -12
  190. build/lib/clitt/addons/appman/__init__.py +0 -13
  191. build/lib/clitt/addons/appman/appman.py +0 -305
  192. build/lib/clitt/addons/appman/appman_enums.py +0 -39
  193. build/lib/clitt/addons/appman/templates/__init__.py +0 -11
  194. build/lib/clitt/addons/widman/__init__.py +0 -14
  195. build/lib/clitt/addons/widman/widget.py +0 -70
  196. build/lib/clitt/addons/widman/widget_entry.py +0 -54
  197. build/lib/clitt/addons/widman/widgets/__init__.py +0 -14
  198. build/lib/clitt/addons/widman/widgets/widget_free.py +0 -110
  199. build/lib/clitt/addons/widman/widgets/widget_punch.py +0 -246
  200. build/lib/clitt/addons/widman/widgets/widget_send_msg.py +0 -272
  201. build/lib/clitt/addons/widman/widgets/widget_time_calc.py +0 -146
  202. build/lib/clitt/addons/widman/widman.py +0 -123
  203. build/lib/clitt/core/__init__.py +0 -15
  204. build/lib/clitt/core/exception/__init__.py +0 -11
  205. build/lib/clitt/core/exception/exceptions.py +0 -19
  206. build/lib/clitt/core/icons/__init__.py +0 -12
  207. build/lib/clitt/core/icons/emojis/__init__.py +0 -12
  208. build/lib/clitt/core/icons/emojis/emojis.py +0 -41
  209. build/lib/clitt/core/icons/emojis/face_smiling.py +0 -40
  210. build/lib/clitt/core/icons/font_awesome/__init__.py +0 -18
  211. build/lib/clitt/core/icons/font_awesome/app_icons.py +0 -55
  212. build/lib/clitt/core/icons/font_awesome/awesome.py +0 -76
  213. build/lib/clitt/core/icons/font_awesome/dashboard_icons.py +0 -93
  214. build/lib/clitt/core/icons/font_awesome/form_icons.py +0 -69
  215. build/lib/clitt/core/icons/font_awesome/game_icons.py +0 -59
  216. build/lib/clitt/core/icons/font_awesome/nav_icons.py +0 -42
  217. build/lib/clitt/core/icons/font_awesome/trickplay_icons.py +0 -39
  218. build/lib/clitt/core/icons/font_awesome/widget_icons.py +0 -37
  219. build/lib/clitt/core/preferences.py +0 -87
  220. build/lib/clitt/core/term/__init__.py +0 -14
  221. build/lib/clitt/core/term/commons.py +0 -106
  222. build/lib/clitt/core/term/cursor.py +0 -174
  223. build/lib/clitt/core/term/screen.py +0 -106
  224. build/lib/clitt/core/term/terminal.py +0 -202
  225. build/lib/clitt/core/tui/__init__.py +0 -20
  226. build/lib/clitt/core/tui/line_input/__init__.py +0 -12
  227. build/lib/clitt/core/tui/line_input/keyboard_input.py +0 -229
  228. build/lib/clitt/core/tui/line_input/line_input.py +0 -31
  229. build/lib/clitt/core/tui/mchoose/__init__.py +0 -12
  230. build/lib/clitt/core/tui/mchoose/mchoose.py +0 -43
  231. build/lib/clitt/core/tui/mchoose/menu_choose.py +0 -192
  232. build/lib/clitt/core/tui/mdashboard/__init__.py +0 -14
  233. build/lib/clitt/core/tui/mdashboard/dashboard_builder.py +0 -54
  234. build/lib/clitt/core/tui/mdashboard/dashboard_item.py +0 -31
  235. build/lib/clitt/core/tui/mdashboard/mdashboard.py +0 -26
  236. build/lib/clitt/core/tui/mdashboard/menu_dashboard.py +0 -148
  237. build/lib/clitt/core/tui/menu/__init__.py +0 -16
  238. build/lib/clitt/core/tui/menu/tui_menu.py +0 -111
  239. build/lib/clitt/core/tui/menu/tui_menu_action.py +0 -47
  240. build/lib/clitt/core/tui/menu/tui_menu_factory.py +0 -117
  241. build/lib/clitt/core/tui/menu/tui_menu_item.py +0 -196
  242. build/lib/clitt/core/tui/menu/tui_menu_ui.py +0 -98
  243. build/lib/clitt/core/tui/menu/tui_menu_view.py +0 -57
  244. build/lib/clitt/core/tui/minput/__init__.py +0 -19
  245. build/lib/clitt/core/tui/minput/access_type.py +0 -26
  246. build/lib/clitt/core/tui/minput/field_builder.py +0 -117
  247. build/lib/clitt/core/tui/minput/form_builder.py +0 -72
  248. build/lib/clitt/core/tui/minput/form_field.py +0 -180
  249. build/lib/clitt/core/tui/minput/input_type.py +0 -30
  250. build/lib/clitt/core/tui/minput/input_validator.py +0 -98
  251. build/lib/clitt/core/tui/minput/menu_input.py +0 -292
  252. build/lib/clitt/core/tui/minput/minput.py +0 -44
  253. build/lib/clitt/core/tui/minput/minput_utils.py +0 -156
  254. build/lib/clitt/core/tui/mselect/__init__.py +0 -12
  255. build/lib/clitt/core/tui/mselect/menu_select.py +0 -170
  256. build/lib/clitt/core/tui/mselect/mselect.py +0 -36
  257. build/lib/clitt/core/tui/table/__init__.py +0 -12
  258. build/lib/clitt/core/tui/table/table_enums.py +0 -47
  259. build/lib/clitt/core/tui/table/table_renderer.py +0 -339
  260. build/lib/clitt/core/tui/tui_application.py +0 -52
  261. build/lib/clitt/core/tui/tui_component.py +0 -154
  262. build/lib/clitt/core/tui/tui_preferences.py +0 -103
  263. build/lib/clitt/utils/__init__.py +0 -11
  264. build/lib/clitt/utils/git_utils.py +0 -66
  265. hspylib_clitt-0.9.120.dist-info/RECORD +0 -334
  266. {hspylib_clitt-0.9.120.dist-info → hspylib_clitt-0.9.122.dist-info}/WHEEL +0 -0
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib
6
- @package: clitt.core.tui.minput
7
- @file: form_builder.py
8
- @created: Thu, 20 May 2021
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.core.tui.minput.field_builder import FieldBuilder
16
- from clitt.core.tui.minput.form_field import FormField
17
- from clitt.core.tui.minput.input_validator import InputValidator
18
- from hspylib.core.tools.dict_tools import get_or_default
19
- from typing import Any, List
20
-
21
- import re
22
-
23
-
24
- class FormBuilder:
25
- """MenuInput form builder."""
26
-
27
- def __init__(self) -> None:
28
- self.fields = []
29
- self._fn_validate = None
30
-
31
- @staticmethod
32
- def get_attr(parts: List[str], index: int, default_value: Any = None) -> str:
33
- return get_or_default(parts, index, default_value) or default_value
34
-
35
- def from_tokenized(self, minput_tokens: List[str], separator: chr = "|") -> "FormBuilder":
36
- """Construct the forms based on string tokens.
37
-
38
- Field tokens (in-order):
39
- <Label> : The field label. Consisting only of alphanumeric characters and under‐scores.
40
- [Mode] : The input mode. One of {[text]|password|checkbox|select}.
41
- [Type] : The input type. One of {letters|numbers|words|[anything]}.
42
- [Min/Max len] : The minimum and maximum length of characters allowed. Defaults to [0/30].
43
- [Perm] : The field permissions. One of {r|[rw]}. Where \"r\" for Read Only ; \"rw\" for Read & Write.
44
- [Value] : The initial value of the field. This field may not be blank if the field is read only.
45
- """
46
- for tk_field in minput_tokens:
47
- parts = list(map(str.strip, tk_field.split(separator)))
48
- validator_fn = getattr(InputValidator, self.get_attr(parts, 2, "anything"))
49
- min_max = list(map(int, map(str.strip, self.get_attr(parts, 3, "5/30").split("/"))))
50
- access = re.sub("^rw$", "read-write", self.get_attr(parts, 4, "rw"))
51
- access = re.sub("^r$", "read-only", access)
52
- # fmt: off
53
- self.field() \
54
- .label(self.get_attr(parts, 0, 'Label')) \
55
- .itype(self.get_attr(parts, 1, 'text')) \
56
- .min_max_length(
57
- get_or_default(min_max, 0, 5),
58
- get_or_default(min_max, 1, 30)
59
- ) \
60
- .access_type(access) \
61
- .validator(validator_fn()) \
62
- .value(self.get_attr(parts, 5, None)) \
63
- .build()
64
- # fmt: on
65
-
66
- return self
67
-
68
- def field(self) -> Any:
69
- return FieldBuilder(self)
70
-
71
- def build(self) -> List[FormField]:
72
- return self.fields
@@ -1,180 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib
6
- @package: clitt.core.tui.minput
7
- @file: form_field.py
8
- @created: Thu, 20 May 2021
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.core.icons.font_awesome.form_icons import FormIcons
16
- from clitt.core.tui.minput.access_type import AccessType
17
- from clitt.core.tui.minput.input_type import InputType
18
- from clitt.core.tui.minput.input_validator import InputValidator
19
- from clitt.core.tui.minput.minput_utils import get_selected, MASK_SYMBOLS, toggle_selected, unpack_masked
20
- from hspylib.core.exception.exceptions import InvalidInputError
21
- from hspylib.core.tools.text_tools import xstr
22
- from typing import Any, Callable, Optional, TypeAlias
23
-
24
- FieldValidator_Fn: TypeAlias = Callable[["FormField"], bool]
25
-
26
-
27
- class FormField:
28
- """Represent a MenuInput form field."""
29
-
30
- def __init__(
31
- self,
32
- label: str = None,
33
- dest: str = None,
34
- itype: InputType = InputType.TEXT,
35
- min_length: int = 5,
36
- max_length: int = 30,
37
- access_type: AccessType = AccessType.READ_WRITE,
38
- value: Any = "",
39
- input_validator: InputValidator = InputValidator.anything(),
40
- field_validator: FieldValidator_Fn = None,
41
- tooltip: str = None,
42
- ):
43
- self._label = label
44
- self._tooltip = tooltip
45
- self._dest = dest
46
- self._itype = itype
47
- self._min_length = min_length
48
- self._max_length = max_length
49
- self._access_type = access_type
50
- self.input_validator = input_validator
51
- self.field_validator = field_validator
52
- self._value = self.assign(value)
53
-
54
- def __str__(self) -> str:
55
- return (
56
- f"[{self.label}, {self.itype.name}({self.min_length}/{self.max_length})"
57
- f"<{self.access_type}> = '{self.value or ''}']"
58
- )
59
-
60
- def __repr__(self):
61
- return str(self)
62
-
63
- @property
64
- def label(self) -> str:
65
- return self._label
66
-
67
- @property
68
- def tooltip(self) -> str:
69
- return self._tooltip
70
-
71
- @property
72
- def dest(self) -> str:
73
- return self._dest
74
-
75
- @property
76
- def itype(self) -> InputType:
77
- return self._itype
78
-
79
- @property
80
- def min_length(self) -> int:
81
- return self._min_length
82
-
83
- @property
84
- def max_length(self) -> int:
85
- return self._max_length
86
-
87
- @property
88
- def access_type(self) -> AccessType:
89
- return self._access_type
90
-
91
- @property
92
- def value(self) -> Optional[Any]:
93
- return self._value
94
-
95
- @value.setter
96
- def value(self, new_value: Any) -> None:
97
- self._value = self.assign(new_value)
98
-
99
- @property
100
- def length(self) -> int:
101
- """Get the field real length, depending on the field type."""
102
- real_value = str(self.value or "")
103
- match self.itype:
104
- case InputType.CHECKBOX:
105
- real_value = "1"
106
- case InputType.SELECT:
107
- _, real_value = get_selected(str(self.value))
108
- case InputType.MASKED:
109
- real_value, mask = unpack_masked(str(self.value))
110
- idx = len(real_value)
111
- while idx < len(mask) and mask[idx] not in MASK_SYMBOLS:
112
- idx += 1
113
- return idx
114
-
115
- return len(real_value)
116
-
117
- @property
118
- def icon(self) -> FormIcons:
119
- if self.access_type == AccessType.READ_ONLY:
120
- return FormIcons.LOCKED
121
- match self.itype:
122
- case InputType.PASSWORD:
123
- icon = FormIcons.HIDDEN
124
- case InputType.CHECKBOX:
125
- icon = FormIcons.MARKED
126
- case InputType.SELECT:
127
- icon = FormIcons.SELECTABLE
128
- case InputType.MASKED:
129
- icon = FormIcons.MASKED
130
- case InputType.TEXT:
131
- if self.input_validator.pattern_type == InputValidator.PatternType.NUMBERS:
132
- icon = FormIcons.NUMBERS
133
- elif self.input_validator.pattern_type == InputValidator.PatternType.LETTERS:
134
- icon = FormIcons.LETTERS
135
- elif self.input_validator.pattern_type == InputValidator.PatternType.WORDS:
136
- icon = FormIcons.WORDS
137
- elif self.input_validator.pattern_type == InputValidator.PatternType.CUSTOM:
138
- icon = FormIcons.CUSTOM
139
- else:
140
- icon = FormIcons.EDITABLE
141
- case _:
142
- icon = FormIcons.QUESTION_CIRCLE
143
- return icon
144
-
145
- def can_write(self) -> bool:
146
- """Whether this field value can be set or not."""
147
- return self.access_type == AccessType.READ_WRITE
148
-
149
- def assign(self, value: Any) -> Any:
150
- """Assign a value for this field. Must match the input validator, otherwise an exception will be thrown.
151
- :param value: TODO
152
- """
153
- valid = True
154
- if value is not None and self.input_validator:
155
- match self.itype:
156
- case InputType.MASKED:
157
- unpack_masked(value)
158
- case InputType.SELECT:
159
- toggle_selected(value)
160
- case InputType.CHECKBOX:
161
- valid = isinstance(value, bool)
162
- case _:
163
- valid = all(self.validate_input(val) for val in xstr(value))
164
-
165
- if not valid:
166
- raise InvalidInputError(f"Value {value} is invalid!")
167
-
168
- self._value = value
169
-
170
- return self._value
171
-
172
- def validate_input(self, value: Any = None) -> bool:
173
- """Validate the input using the assigned validator.
174
- :param value: the value to validate against.
175
- """
176
- return self.input_validator(str(value)) if self.input_validator else True
177
-
178
- def validate_field(self) -> bool:
179
- """Validate the field using the assigned validator function."""
180
- return self.field_validator(self) if self.field_validator else False
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib
6
- @package: clitt.core.tui.minput
7
- @file: input_type.py
8
- @created: Thu, 20 May 2021
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from hspylib.core.enums.enumeration import Enumeration
16
-
17
-
18
- class InputType(Enumeration):
19
- """MenuInput input field types."""
20
-
21
- # fmt: off
22
- TEXT = "text"
23
- PASSWORD = "password"
24
- CHECKBOX = "checkbox"
25
- SELECT = "select"
26
- MASKED = "masked"
27
- # fmt: on
28
-
29
- def __repr__(self):
30
- return str(self)
@@ -1,98 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib
6
- @package: clitt.core.tui.minput
7
- @file: input_validator.py
8
- @created: Thu, 20 May 2021
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
-
16
- from hspylib.core.enums.enumeration import Enumeration
17
- from hspylib.core.tools.validator import Validator
18
-
19
- import re
20
-
21
-
22
- class InputValidator(Validator):
23
- """MenuInput 'input' validator."""
24
-
25
- class PatternType(Enumeration):
26
- # fmt: off
27
- CUSTOM = r'' # It will be set later
28
- ANYTHING = r'.'
29
- LETTERS = r'[a-zA-Z]'
30
- WORDS = r'[a-zA-Z0-9 _]'
31
- NUMBERS = r'[0-9\.\,]'
32
- MASKED = r'[a-zA-Z0-9]'
33
- # fmt: on
34
-
35
- @classmethod
36
- def custom(cls, pattern: str) -> "InputValidator":
37
- """Return a custom validator that allows customize the input rules.
38
- :param pattern: the custom validator pattern.
39
- """
40
- pattern_type = cls.PatternType.CUSTOM
41
- validator = InputValidator(pattern_type=pattern_type)
42
- validator.pattern = pattern
43
- return validator
44
-
45
- @classmethod
46
- def letters(cls) -> "InputValidator":
47
- """Return a validator that allows only letters."""
48
- return InputValidator(cls.PatternType.LETTERS)
49
-
50
- @classmethod
51
- def numbers(cls) -> "InputValidator":
52
- """Return a validator that allows only numbers."""
53
- return InputValidator(cls.PatternType.NUMBERS)
54
-
55
- @classmethod
56
- def words(cls) -> "InputValidator":
57
- """Return a validator that allows only words (space, numbers or letters)."""
58
- return InputValidator(cls.PatternType.WORDS)
59
-
60
- @classmethod
61
- def anything(cls) -> "InputValidator":
62
- """Return a validator that allows any input value."""
63
- return InputValidator(cls.PatternType.ANYTHING)
64
-
65
- @classmethod
66
- def masked(cls) -> "InputValidator":
67
- """Return a validator that allows masked inputs."""
68
- return InputValidator(cls.PatternType.MASKED)
69
-
70
- def __init__(self, pattern_type: PatternType = PatternType.ANYTHING):
71
- self._pattern_type = pattern_type
72
- self._pattern = pattern_type.value
73
-
74
- def __str__(self) -> str:
75
- return f'r"{self.pattern}"' if self.pattern_type == self.PatternType.CUSTOM else self.pattern_type.name
76
-
77
- def __repr__(self):
78
- return str(self)
79
-
80
- def __call__(self, *args, **kwargs) -> bool:
81
- return all(self.validate(value) for value in args)
82
-
83
- def validate(self, value: str) -> bool:
84
- """Validate the value against the validator pattern."""
85
- unmasked_value = re.split("[|,;]", value)[0] if value else ""
86
- return bool(re.match(self.pattern, unmasked_value))
87
-
88
- @property
89
- def pattern(self) -> str:
90
- return str(self._pattern)
91
-
92
- @pattern.setter
93
- def pattern(self, pattern: str) -> None:
94
- self._pattern = rf"{pattern}"
95
-
96
- @property
97
- def pattern_type(self) -> PatternType:
98
- return self._pattern_type
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
-
4
- """
5
- @project: HsPyLib
6
- @package: clitt.core.tui.minput
7
- @file: menu_input.py
8
- @created: Wed, 17 May 2023
9
- @author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
10
- @site: https://github.com/yorevs/hspylib
11
- @license: MIT - Please refer to <https://opensource.org/licenses/MIT>
12
-
13
- Copyright·(c)·2024,·HSPyLib
14
- """
15
- from clitt.core.icons.font_awesome.form_icons import FormIcons
16
- from clitt.core.icons.font_awesome.nav_icons import NavIcons
17
- from clitt.core.term.commons import Direction, get_cursor_position
18
- from clitt.core.term.terminal import Terminal
19
- from clitt.core.tui.minput import minput_utils as mu
20
- from clitt.core.tui.minput.form_builder import FormBuilder
21
- from clitt.core.tui.minput.form_field import FormField
22
- from clitt.core.tui.minput.input_type import InputType
23
- from clitt.core.tui.minput.minput_utils import MASK_SYMBOLS, VALUE_SEPARATORS
24
- from clitt.core.tui.tui_component import TUIComponent
25
- from functools import reduce
26
- from hspylib.core.exception.exceptions import InvalidInputError, InvalidStateError
27
- from hspylib.core.namespace import Namespace
28
- from hspylib.core.tools.text_tools import xstr
29
- from hspylib.modules.cli.keyboard import Keyboard
30
- from operator import add
31
- from time import sleep
32
- from typing import List, Optional
33
-
34
- import pyperclip
35
- import re
36
-
37
-
38
- class MenuInput(TUIComponent):
39
- """Provide a form input for terminal UIs."""
40
-
41
- NAV_ICONS = NavIcons.compose(NavIcons.UP, NavIcons.DOWN)
42
-
43
- @classmethod
44
- def builder(cls) -> FormBuilder:
45
- return FormBuilder()
46
-
47
- def __init__(self, title: str, fields: List[FormField]):
48
- super().__init__(title)
49
- self.fields = fields
50
- self.positions = [(0, 0) for _ in fields]
51
- self.cur_field = self._done = None
52
- self.cur_row = self.cur_col = self.tab_index = 0
53
- self.max_label_length = max(len(field.label) for field in fields)
54
- self.max_value_length = max(field.max_length for field in fields)
55
- self.max_detail_length = max(mu.detail_len(field) for field in fields)
56
-
57
- def execute(self) -> Optional[Namespace]:
58
- if len(self.fields) == 0:
59
- return None
60
-
61
- self._prepare_render()
62
- keypress = self._loop()
63
-
64
- if keypress == Keyboard.VK_ENTER:
65
- form_fields = Namespace("FormFields")
66
- for field in self.fields:
67
- form_fields.setattr(field.dest, field.value)
68
- return form_fields
69
-
70
- return None
71
-
72
- def render(self) -> None:
73
-
74
- Terminal.set_show_cursor(False)
75
- self.cursor.restore()
76
- self.writeln(f"{self.prefs.title_color.placeholder}{self.title}%EOL%%NC%")
77
-
78
- for idx, field in enumerate(self.fields):
79
- field_length = field.length
80
- if self.tab_index == idx:
81
- mu.mi_print(
82
- self.screen, f" {field.label}", self.prefs.sel_bg_color.placeholder, self.max_label_length + 2
83
- )
84
- self.cur_field = field
85
- else:
86
- mu.mi_print(self.screen, f" {field.label}", field_len=self.max_label_length + 2)
87
- self._buffer_positions(idx, field_length)
88
- self._render_field(field)
89
- self._render_details(field, field_length)
90
-
91
- self.cursor.erase(Direction.DOWN)
92
- self.draw_navbar(self.navbar())
93
- self._set_cursor_pos()
94
- self._re_render = False
95
- Terminal.set_show_cursor()
96
-
97
- def navbar(self, **kwargs) -> str:
98
-
99
- return (
100
- f"%EOL%{NavIcons.POINTER} %GREEN%{self.cur_field.tooltip or f'the {self.cur_field.label.lower()}'}%NC%"
101
- f"%EOL%{self.prefs.navbar_color.placeholder}%EOL%"
102
- f"[Enter] Submit [{self.NAV_ICONS}] "
103
- f"Navigate [{NavIcons.TAB}] Next [Space] Toggle [^P] Paste [Esc] Quit %NC%"
104
- )
105
-
106
- def handle_keypress(self) -> Keyboard:
107
- length = len(self.fields)
108
-
109
- if keypress := Keyboard.wait_keystroke():
110
- match keypress:
111
- case Keyboard.VK_ESC:
112
- self._done = True
113
- case _ as key if key in [Keyboard.VK_TAB, Keyboard.VK_DOWN]:
114
- self.tab_index = min(length - 1, self.tab_index + 1)
115
- case _ as key if key in [Keyboard.VK_SHIFT_TAB, Keyboard.VK_UP]:
116
- self.tab_index = max(0, self.tab_index - 1)
117
- case Keyboard.VK_BACKSPACE:
118
- if not self.cur_field.can_write():
119
- self._display_error("This field is read only !")
120
- else:
121
- self._handle_backspace()
122
- case Keyboard.VK_CTRL_P:
123
- self._handle_ctrl_p()
124
- case Keyboard.VK_ENTER:
125
- return self._handle_enter()
126
- case _ as key if key.isalnum() or key.ispunct() or key == Keyboard.VK_SPACE:
127
- if not self.cur_field.can_write():
128
- self._display_error("This field is read only !")
129
- else:
130
- self._handle_input(keypress)
131
-
132
- self._re_render = True
133
-
134
- return keypress
135
-
136
- def _handle_enter(self) -> Optional[Keyboard]:
137
- """Handle 'enter' press. Validate & Save form and exit."""
138
- invalid = next((field for field in self.fields if not field.validate_field()), None)
139
- if invalid:
140
- idx = self.fields.index(invalid)
141
- pos = self.positions[idx]
142
- self.cur_row = pos[0]
143
- self.tab_index = idx
144
- self._display_error("Form field is not valid: " + str(invalid))
145
- return None
146
-
147
- for idx, field in enumerate(self.fields):
148
- match field.itype:
149
- case InputType.MASKED:
150
- field.value = re.split(VALUE_SEPARATORS, field.value)[0]
151
- case InputType.CHECKBOX:
152
- field.value = bool(field.value)
153
- case InputType.SELECT:
154
- _, field.value = mu.get_selected(field.value)
155
- self._done = True
156
- return Keyboard.VK_ENTER
157
-
158
- def _handle_input(self, keypress: Keyboard) -> None:
159
- """Handle a form input.
160
- :param keypress: the input provided by the keypress.
161
- """
162
-
163
- match self.cur_field.itype:
164
- case InputType.CHECKBOX:
165
- if keypress == Keyboard.VK_SPACE:
166
- self.cur_field.value = not self.cur_field.value
167
- case InputType.SELECT:
168
- if keypress == Keyboard.VK_SPACE:
169
- if self.cur_field.value:
170
- self.cur_field.value = mu.toggle_selected(xstr(self.cur_field.value))
171
- case InputType.MASKED:
172
- value, mask = mu.unpack_masked(xstr(self.cur_field.value))
173
- try:
174
- self.cur_field.value = mu.append_masked(value, mask, keypress.value)
175
- except InvalidInputError as err:
176
- self._display_error(str(err))
177
- case _:
178
- if len(xstr(self.cur_field.value)) < self.cur_field.max_length:
179
- if self.cur_field.validate_input(keypress.value):
180
- self.cur_field.value = (str(self.cur_field.value) if self.cur_field.value else "") + str(
181
- keypress.value
182
- )
183
- else:
184
- self._display_error(
185
- f"Input '{keypress.value}' is invalid. "
186
- f"This field takes only {self.cur_field.input_validator}!"
187
- )
188
-
189
- def _handle_backspace(self) -> None:
190
- """Handle 'backspace' press. Delete previous input."""
191
-
192
- if self.cur_field.itype == InputType.MASKED:
193
- value, mask = mu.unpack_masked(str(self.cur_field.value))
194
- value = value[:-1]
195
- while mask[len(value) - 1] not in MASK_SYMBOLS:
196
- value = value[:-1]
197
- self.cur_field.value = f"{value}|{mask}"
198
- elif self.cur_field.itype not in [InputType.CHECKBOX, InputType.SELECT]:
199
- if self.cur_field.can_write() and len(str(self.cur_field.value)) >= 1:
200
- self.cur_field.value = str(self.cur_field.value)[:-1]
201
- elif not self.cur_field.can_write():
202
- self._display_error("This field is read only !")
203
-
204
- def _handle_ctrl_p(self) -> None:
205
- """Handle 'ctrl + p' press. Paste content from clipboard."""
206
- for c in pyperclip.paste():
207
- self._handle_input(Keyboard.of_value(c))
208
-
209
- def _set_cursor_pos(self) -> None:
210
- """Set the cursor at the right position according to the ATB index."""
211
- pos = self.positions[self.tab_index]
212
- self.screen.cursor.move_to(pos[0], pos[1] + self.cur_field.length)
213
-
214
- def _render_field(self, field: FormField) -> None:
215
- """Render the specified form field.
216
- :param field: the form field to render.
217
- """
218
- match field.itype:
219
- case InputType.TEXT:
220
- mu.mi_print(self.screen, xstr(field.value), field_len=self.max_value_length)
221
- case InputType.PASSWORD:
222
- mu.mi_print(self.screen, "*" * field.length, field_len=self.max_value_length)
223
- case InputType.CHECKBOX:
224
- mu.mi_print(
225
- self.screen,
226
- " ",
227
- FormIcons.CHECK_SQUARE.unicode if field.value else FormIcons.UNCHECK_SQUARE.unicode,
228
- field_len=self.max_value_length - 1,
229
- )
230
- case InputType.SELECT:
231
- if field.value:
232
- mat = re.search(rf".*({VALUE_SEPARATORS})?<(.+)>({VALUE_SEPARATORS})?.*", str(field.value))
233
- sel_value = mat.group(2) if mat else re.split(VALUE_SEPARATORS, str(field.value))[0]
234
- mu.mi_print(self.screen, f"{sel_value}", field_len=self.max_value_length)
235
- case InputType.MASKED:
236
- value, mask = mu.unpack_masked(str(field.value))
237
- mu.mi_print(self.screen, mu.over_masked(value, mask), field_len=self.max_value_length)
238
- case _:
239
- raise InvalidStateError(f"Invalid form field type: {field.itype}")
240
-
241
- def _render_details(self, field: FormField, field_details: int) -> None:
242
- """Render details about total/remaining field characters.
243
- :param field: the form field to render.
244
- :param field_details: details about the form field (total / remaining) size.
245
- """
246
- padding = 1 - len(str(self.max_detail_length / 2))
247
- fmt = "{:<3}{:>" + str(padding) + "}/{:<" + str(padding) + "} %NC%"
248
- if field.itype == InputType.SELECT:
249
- idx, _ = mu.get_selected(field.value)
250
- count = len(re.split(VALUE_SEPARATORS, field.value))
251
- self.writeln(fmt.format(field.icon, idx + 1 if idx >= 0 else 1, count))
252
- elif field.itype == InputType.MASKED:
253
- value, mask = mu.unpack_masked(xstr(field.value))
254
- self.writeln(
255
- fmt.format(
256
- field.icon,
257
- reduce(add, list(map(mask[: len(value)].count, MASK_SYMBOLS))),
258
- reduce(add, list(map(mask.count, MASK_SYMBOLS))),
259
- )
260
- )
261
- else:
262
- self.writeln(fmt.format(field.icon, field_details, field.max_length))
263
-
264
- def _buffer_positions(self, field_index: int, field_size: int) -> None:
265
- """Buffer all cursor positions to avoid calling get_cursor_pos over and over because it is a very
266
- expensive call.
267
- :param field_index: the current form field index.
268
- :param field_size: the form field length.
269
- """
270
- if f_pos := get_cursor_position() if self.positions[field_index] == (0, 0) else self.positions[field_index]:
271
- self.positions[field_index] = f_pos
272
- if self.tab_index == field_index:
273
- self.cur_row, self.cur_col = f_pos[0], f_pos[1] + field_size
274
-
275
- def _display_error(self, err_msg: str) -> None:
276
- """Display a form filling or submitting error.
277
- :param err_msg: the error message.
278
- """
279
- Terminal.set_enable_echo(False)
280
- Terminal.set_show_cursor(False)
281
- offset = 15 # Sum of minput used characters that are not related to the field.
282
- err_pos = offset + self.max_label_length + self.max_value_length + self.max_detail_length
283
- self.cursor.move_to(self.cur_row, err_pos)
284
- mu.mi_print_err(self.screen, f"{FormIcons.ARROW_LEFT} {err_msg}")
285
- # This calculation gives a good delay amount based on the size of the message.
286
- sleep(max(2.0, int(len(err_msg) / 21)))
287
- Terminal.set_enable_echo()
288
- # Erase the message after the timeout
289
- self.cursor.move_to(self.cur_row, err_pos)
290
- self.cursor.erase(Direction.RIGHT)
291
- Terminal.set_show_cursor()
292
- self._re_render = True