bootstack 0.1.0__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.
Files changed (471) hide show
  1. bootstack/__init__.py +157 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/_core/__init__.py +21 -0
  4. bootstack/_core/capabilities/__init__.py +45 -0
  5. bootstack/_core/capabilities/after.py +103 -0
  6. bootstack/_core/capabilities/bind.py +192 -0
  7. bootstack/_core/capabilities/bindtags.py +112 -0
  8. bootstack/_core/capabilities/busy.py +72 -0
  9. bootstack/_core/capabilities/clipboard.py +89 -0
  10. bootstack/_core/capabilities/focus.py +118 -0
  11. bootstack/_core/capabilities/grab.py +65 -0
  12. bootstack/_core/capabilities/grid.py +211 -0
  13. bootstack/_core/capabilities/localization.py +231 -0
  14. bootstack/_core/capabilities/pack.py +140 -0
  15. bootstack/_core/capabilities/place.py +113 -0
  16. bootstack/_core/capabilities/selection.py +136 -0
  17. bootstack/_core/capabilities/signals.py +244 -0
  18. bootstack/_core/capabilities/winfo.py +315 -0
  19. bootstack/_core/colorutils.py +234 -0
  20. bootstack/_core/exceptions.py +25 -0
  21. bootstack/_core/images.py +463 -0
  22. bootstack/_core/mixins/__init__.py +1 -0
  23. bootstack/_core/mixins/ttk_state.py +35 -0
  24. bootstack/_core/mixins/widget.py +132 -0
  25. bootstack/_core/paths.py +49 -0
  26. bootstack/_core/publisher.py +149 -0
  27. bootstack/_core/variables.py +62 -0
  28. bootstack/_runtime/__init__.py +3 -0
  29. bootstack/_runtime/app.py +930 -0
  30. bootstack/_runtime/base_window.py +945 -0
  31. bootstack/_runtime/events.py +399 -0
  32. bootstack/_runtime/shortcuts.py +496 -0
  33. bootstack/_runtime/tk_patch.py +43 -0
  34. bootstack/_runtime/toplevel.py +276 -0
  35. bootstack/_runtime/utility.py +457 -0
  36. bootstack/_runtime/visual_focus.py +240 -0
  37. bootstack/_runtime/window_utilities.py +1123 -0
  38. bootstack/assets/__init__.py +21 -0
  39. bootstack/assets/bootstack.ico +0 -0
  40. bootstack/assets/bootstack.png +0 -0
  41. bootstack/assets/elements/__init__.py +0 -0
  42. bootstack/assets/elements/badge-pill.png +0 -0
  43. bootstack/assets/elements/badge-square.png +0 -0
  44. bootstack/assets/elements/button-compact.png +0 -0
  45. bootstack/assets/elements/button-default.png +0 -0
  46. bootstack/assets/elements/buttongroup-after-h-compact.png +0 -0
  47. bootstack/assets/elements/buttongroup-after-h-default.png +0 -0
  48. bootstack/assets/elements/buttongroup-after-v-compact.png +0 -0
  49. bootstack/assets/elements/buttongroup-after-v-default.png +0 -0
  50. bootstack/assets/elements/buttongroup-before-h-compact.png +0 -0
  51. bootstack/assets/elements/buttongroup-before-h-default.png +0 -0
  52. bootstack/assets/elements/buttongroup-before-v-compact.png +0 -0
  53. bootstack/assets/elements/buttongroup-before-v-default.png +0 -0
  54. bootstack/assets/elements/buttongroup-center-h-compact.png +0 -0
  55. bootstack/assets/elements/buttongroup-center-h-default.png +0 -0
  56. bootstack/assets/elements/buttongroup-center-v-compact.png +0 -0
  57. bootstack/assets/elements/buttongroup-center-v-default.png +0 -0
  58. bootstack/assets/elements/card.png +0 -0
  59. bootstack/assets/elements/checkbox-checked.png +0 -0
  60. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  61. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  62. bootstack/assets/elements/field.png +0 -0
  63. bootstack/assets/elements/input-addon-compact.png +0 -0
  64. bootstack/assets/elements/input-addon-default.png +0 -0
  65. bootstack/assets/elements/input-compact.png +0 -0
  66. bootstack/assets/elements/input-default.png +0 -0
  67. bootstack/assets/elements/list-item-separated.png +0 -0
  68. bootstack/assets/elements/list-item.png +0 -0
  69. bootstack/assets/elements/listrow-compact.png +0 -0
  70. bootstack/assets/elements/listrow-default.png +0 -0
  71. bootstack/assets/elements/manifest.toml +361 -0
  72. bootstack/assets/elements/menu-item.png +0 -0
  73. bootstack/assets/elements/navitem-compact.png +0 -0
  74. bootstack/assets/elements/navitem-default.png +0 -0
  75. bootstack/assets/elements/progressbar-h-compact.png +0 -0
  76. bootstack/assets/elements/progressbar-h-default.png +0 -0
  77. bootstack/assets/elements/progressbar-v-compact.png +0 -0
  78. bootstack/assets/elements/progressbar-v-default.png +0 -0
  79. bootstack/assets/elements/radiobutton.png +0 -0
  80. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  81. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  82. bootstack/assets/elements/slider-handle.png +0 -0
  83. bootstack/assets/elements/slider-track-h.png +0 -0
  84. bootstack/assets/elements/slider-track-v.png +0 -0
  85. bootstack/assets/elements/switch-off.png +0 -0
  86. bootstack/assets/elements/switch-on.png +0 -0
  87. bootstack/assets/elements/tab-h.png +0 -0
  88. bootstack/assets/elements/tab-v.png +0 -0
  89. bootstack/assets/icons/bootstrap.ttf +0 -0
  90. bootstack/assets/icons/glyphmap.json +2080 -0
  91. bootstack/assets/icons/icon_metrics.json +12470 -0
  92. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  93. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +856 -0
  94. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.mo +0 -0
  95. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +878 -0
  96. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  97. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +856 -0
  98. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  99. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +856 -0
  100. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  101. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +856 -0
  102. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  103. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +878 -0
  104. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  105. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +856 -0
  106. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  107. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +856 -0
  108. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  109. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +854 -0
  110. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  111. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +845 -0
  112. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  113. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +844 -0
  114. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  115. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +917 -0
  116. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  117. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +845 -0
  118. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  119. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +844 -0
  120. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  121. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +844 -0
  122. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  123. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +845 -0
  124. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  125. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +845 -0
  126. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  127. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +845 -0
  128. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  129. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +845 -0
  130. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  131. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +845 -0
  132. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  133. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +845 -0
  134. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  135. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +845 -0
  136. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  137. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +845 -0
  138. bootstack/cli/__init__.py +133 -0
  139. bootstack/cli/__main__.py +6 -0
  140. bootstack/cli/add.py +395 -0
  141. bootstack/cli/appicon.py +285 -0
  142. bootstack/cli/build.py +115 -0
  143. bootstack/cli/config.py +313 -0
  144. bootstack/cli/demo.py +564 -0
  145. bootstack/cli/dev.py +153 -0
  146. bootstack/cli/doctor.py +195 -0
  147. bootstack/cli/icons.py +98 -0
  148. bootstack/cli/promote.py +120 -0
  149. bootstack/cli/pyinstaller.py +268 -0
  150. bootstack/cli/run.py +95 -0
  151. bootstack/cli/start.py +117 -0
  152. bootstack/cli/templates/__init__.py +931 -0
  153. bootstack/clipboard.py +48 -0
  154. bootstack/constants.py +318 -0
  155. bootstack/data/README.md +615 -0
  156. bootstack/data/__init__.py +78 -0
  157. bootstack/data/_observable.py +276 -0
  158. bootstack/data/base.py +780 -0
  159. bootstack/data/file_source.py +367 -0
  160. bootstack/data/memory_source.py +450 -0
  161. bootstack/data/query.py +367 -0
  162. bootstack/data/readers.py +289 -0
  163. bootstack/data/sqlite_source.py +869 -0
  164. bootstack/data/types.py +354 -0
  165. bootstack/data/writers.py +232 -0
  166. bootstack/dev/__init__.py +36 -0
  167. bootstack/dev/_capture.py +141 -0
  168. bootstack/dev/_env.py +43 -0
  169. bootstack/dev/_registry.py +140 -0
  170. bootstack/dev/_reloader.py +351 -0
  171. bootstack/dev/_reset.py +50 -0
  172. bootstack/dev/_watcher.py +91 -0
  173. bootstack/dialogs/__init__.py +948 -0
  174. bootstack/dialogs/_impl/__init__.py +47 -0
  175. bootstack/dialogs/_impl/colorchooser.py +588 -0
  176. bootstack/dialogs/_impl/datedialog.py +450 -0
  177. bootstack/dialogs/_impl/dialog.py +594 -0
  178. bootstack/dialogs/_impl/filterdialog.py +358 -0
  179. bootstack/dialogs/_impl/fontdialog.py +364 -0
  180. bootstack/dialogs/_impl/formdialog.py +564 -0
  181. bootstack/dialogs/_impl/message.py +486 -0
  182. bootstack/dialogs/_impl/query.py +570 -0
  183. bootstack/errors.py +67 -0
  184. bootstack/events/__init__.py +111 -0
  185. bootstack/events/_event.py +135 -0
  186. bootstack/events/_payloads.py +539 -0
  187. bootstack/events/_subscription.py +38 -0
  188. bootstack/i18n/README.md +77 -0
  189. bootstack/i18n/__init__.py +23 -0
  190. bootstack/i18n/catalog.py +121 -0
  191. bootstack/i18n/intl_format.py +584 -0
  192. bootstack/i18n/msgcat.py +425 -0
  193. bootstack/i18n/specs.py +156 -0
  194. bootstack/images.py +563 -0
  195. bootstack/py.typed +1 -0
  196. bootstack/scheduling/__init__.py +11 -0
  197. bootstack/scheduling/_schedule.py +218 -0
  198. bootstack/shortcuts.py +21 -0
  199. bootstack/signals/README.md +98 -0
  200. bootstack/signals/__init__.py +10 -0
  201. bootstack/signals/integration.py +100 -0
  202. bootstack/signals/signal.py +353 -0
  203. bootstack/signals/types.py +6 -0
  204. bootstack/store.py +286 -0
  205. bootstack/streams/__init__.py +12 -0
  206. bootstack/streams/_stream.py +321 -0
  207. bootstack/style/__init__.py +38 -0
  208. bootstack/style/builders/__init__.py +51 -0
  209. bootstack/style/builders/badge.py +46 -0
  210. bootstack/style/builders/button.py +339 -0
  211. bootstack/style/builders/buttongroup.py +311 -0
  212. bootstack/style/builders/calendar.py +271 -0
  213. bootstack/style/builders/checkbutton.py +110 -0
  214. bootstack/style/builders/combobox.py +113 -0
  215. bootstack/style/builders/contextmenu.py +268 -0
  216. bootstack/style/builders/entry.py +82 -0
  217. bootstack/style/builders/expander.py +148 -0
  218. bootstack/style/builders/field.py +335 -0
  219. bootstack/style/builders/frame.py +50 -0
  220. bootstack/style/builders/label.py +28 -0
  221. bootstack/style/builders/labelframe.py +34 -0
  222. bootstack/style/builders/listview.py +369 -0
  223. bootstack/style/builders/menubar.py +91 -0
  224. bootstack/style/builders/menubutton.py +359 -0
  225. bootstack/style/builders/panedwindow.py +25 -0
  226. bootstack/style/builders/progressbar.py +67 -0
  227. bootstack/style/builders/radiobutton.py +99 -0
  228. bootstack/style/builders/scale.py +64 -0
  229. bootstack/style/builders/scrollbar.py +225 -0
  230. bootstack/style/builders/separator.py +49 -0
  231. bootstack/style/builders/sidenav.py +643 -0
  232. bootstack/style/builders/sizegrip.py +15 -0
  233. bootstack/style/builders/spinbox.py +119 -0
  234. bootstack/style/builders/switch.py +70 -0
  235. bootstack/style/builders/tabitem.py +204 -0
  236. bootstack/style/builders/togglegroup.py +294 -0
  237. bootstack/style/builders/toolbutton.py +275 -0
  238. bootstack/style/builders/tooltip.py +26 -0
  239. bootstack/style/builders/treeview.py +193 -0
  240. bootstack/style/builders/utils.py +455 -0
  241. bootstack/style/builders_tk/__init__.py +16 -0
  242. bootstack/style/builders_tk/defaults.py +229 -0
  243. bootstack/style/element.py +173 -0
  244. bootstack/style/fonts.py +123 -0
  245. bootstack/style/style.py +609 -0
  246. bootstack/style/style_builder_base.py +716 -0
  247. bootstack/style/style_builder_mixed.py +93 -0
  248. bootstack/style/style_builder_tk.py +109 -0
  249. bootstack/style/style_builder_ttk.py +353 -0
  250. bootstack/style/style_resolver.py +447 -0
  251. bootstack/style/theme.py +245 -0
  252. bootstack/style/theme_provider.py +471 -0
  253. bootstack/style/themes/__init__.py +128 -0
  254. bootstack/style/tk_patch.py +5 -0
  255. bootstack/style/token_maps.py +41 -0
  256. bootstack/style/types.py +32 -0
  257. bootstack/style/typography.py +523 -0
  258. bootstack/style/utility.py +746 -0
  259. bootstack/types.py +39 -0
  260. bootstack/validation/__init__.py +6 -0
  261. bootstack/validation/types.py +15 -0
  262. bootstack/validation/validation_result.py +17 -0
  263. bootstack/validation/validation_rules.py +205 -0
  264. bootstack/widgets/__init__.py +74 -0
  265. bootstack/widgets/_core/__init__.py +31 -0
  266. bootstack/widgets/_core/app_config.py +266 -0
  267. bootstack/widgets/_core/base.py +461 -0
  268. bootstack/widgets/_core/container.py +334 -0
  269. bootstack/widgets/_core/context.py +35 -0
  270. bootstack/widgets/_core/events.py +130 -0
  271. bootstack/widgets/_core/field_mixin.py +353 -0
  272. bootstack/widgets/_core/icon_image_props.py +72 -0
  273. bootstack/widgets/_core/image_binding.py +93 -0
  274. bootstack/widgets/_core/navmodel.py +345 -0
  275. bootstack/widgets/_core/options.py +210 -0
  276. bootstack/widgets/_core/selection_group.py +130 -0
  277. bootstack/widgets/_core/window_controls.py +96 -0
  278. bootstack/widgets/_core/window_menu.py +246 -0
  279. bootstack/widgets/_impl/__init__.py +1 -0
  280. bootstack/widgets/_impl/_internal/__init__.py +0 -0
  281. bootstack/widgets/_impl/_internal/wrapper_base.py +307 -0
  282. bootstack/widgets/_impl/_parts/__init__.py +11 -0
  283. bootstack/widgets/_impl/_parts/numberentry_part.py +385 -0
  284. bootstack/widgets/_impl/_parts/spinnerentry_part.py +434 -0
  285. bootstack/widgets/_impl/_parts/textentry_part.py +406 -0
  286. bootstack/widgets/_impl/composites/__init__.py +33 -0
  287. bootstack/widgets/_impl/composites/_dateutils.py +33 -0
  288. bootstack/widgets/_impl/composites/_image_fit.py +105 -0
  289. bootstack/widgets/_impl/composites/accordion.py +381 -0
  290. bootstack/widgets/_impl/composites/avatar.py +191 -0
  291. bootstack/widgets/_impl/composites/buttongroup.py +371 -0
  292. bootstack/widgets/_impl/composites/calendar.py +972 -0
  293. bootstack/widgets/_impl/composites/carousel.py +567 -0
  294. bootstack/widgets/_impl/composites/chart.py +882 -0
  295. bootstack/widgets/_impl/composites/compositeframe.py +298 -0
  296. bootstack/widgets/_impl/composites/contextmenu.py +1951 -0
  297. bootstack/widgets/_impl/composites/dateentry.py +404 -0
  298. bootstack/widgets/_impl/composites/dropdownbutton.py +325 -0
  299. bootstack/widgets/_impl/composites/expander.py +515 -0
  300. bootstack/widgets/_impl/composites/field.py +670 -0
  301. bootstack/widgets/_impl/composites/form.py +1066 -0
  302. bootstack/widgets/_impl/composites/gallery.py +551 -0
  303. bootstack/widgets/_impl/composites/list/__init__.py +15 -0
  304. bootstack/widgets/_impl/composites/list/listitem.py +802 -0
  305. bootstack/widgets/_impl/composites/list/listview.py +1433 -0
  306. bootstack/widgets/_impl/composites/menu/__init__.py +18 -0
  307. bootstack/widgets/_impl/composites/menu/model.py +358 -0
  308. bootstack/widgets/_impl/composites/menu/render_native.py +134 -0
  309. bootstack/widgets/_impl/composites/menu/render_themed.py +134 -0
  310. bootstack/widgets/_impl/composites/meter.py +860 -0
  311. bootstack/widgets/_impl/composites/numericentry.py +201 -0
  312. bootstack/widgets/_impl/composites/pagestack.py +395 -0
  313. bootstack/widgets/_impl/composites/passwordentry.py +142 -0
  314. bootstack/widgets/_impl/composites/pathentry.py +168 -0
  315. bootstack/widgets/_impl/composites/picture.py +289 -0
  316. bootstack/widgets/_impl/composites/radiogroup.py +511 -0
  317. bootstack/widgets/_impl/composites/scrolledtext.py +375 -0
  318. bootstack/widgets/_impl/composites/scrolledtext.pyi +186 -0
  319. bootstack/widgets/_impl/composites/scrollview.py +764 -0
  320. bootstack/widgets/_impl/composites/selectbox.py +1026 -0
  321. bootstack/widgets/_impl/composites/shell/__init__.py +40 -0
  322. bootstack/widgets/_impl/composites/shell/content_host.py +52 -0
  323. bootstack/widgets/_impl/composites/shell/layout.py +335 -0
  324. bootstack/widgets/_impl/composites/shell/nav_panel.py +345 -0
  325. bootstack/widgets/_impl/composites/shell/providers.py +558 -0
  326. bootstack/widgets/_impl/composites/shell/rail.py +117 -0
  327. bootstack/widgets/_impl/composites/shell/shell.py +581 -0
  328. bootstack/widgets/_impl/composites/shell/workspace.py +273 -0
  329. bootstack/widgets/_impl/composites/sidenav/__init__.py +16 -0
  330. bootstack/widgets/_impl/composites/sidenav/header.py +81 -0
  331. bootstack/widgets/_impl/composites/sidenav/separator.py +44 -0
  332. bootstack/widgets/_impl/composites/slider/__init__.py +7 -0
  333. bootstack/widgets/_impl/composites/slider/_shared.py +195 -0
  334. bootstack/widgets/_impl/composites/slider/rangeslider.py +982 -0
  335. bootstack/widgets/_impl/composites/slider/slider.py +851 -0
  336. bootstack/widgets/_impl/composites/spinnerentry.py +185 -0
  337. bootstack/widgets/_impl/composites/tableview/__init__.py +5 -0
  338. bootstack/widgets/_impl/composites/tableview/tableview.py +3388 -0
  339. bootstack/widgets/_impl/composites/tableview/types.py +169 -0
  340. bootstack/widgets/_impl/composites/tabs/__init__.py +23 -0
  341. bootstack/widgets/_impl/composites/tabs/tabitem.py +389 -0
  342. bootstack/widgets/_impl/composites/tabs/tabs.py +974 -0
  343. bootstack/widgets/_impl/composites/tabs/tabview.py +650 -0
  344. bootstack/widgets/_impl/composites/textarea/__init__.py +26 -0
  345. bootstack/widgets/_impl/composites/textarea/change.py +46 -0
  346. bootstack/widgets/_impl/composites/textarea/codeeditor.py +526 -0
  347. bootstack/widgets/_impl/composites/textarea/core.py +495 -0
  348. bootstack/widgets/_impl/composites/textarea/decoration.py +42 -0
  349. bootstack/widgets/_impl/composites/textarea/diff.py +127 -0
  350. bootstack/widgets/_impl/composites/textarea/extensions/__init__.py +1 -0
  351. bootstack/widgets/_impl/composites/textarea/extensions/bracket_matcher.py +138 -0
  352. bootstack/widgets/_impl/composites/textarea/extensions/indent_guides.py +158 -0
  353. bootstack/widgets/_impl/composites/textarea/extensions/line_numbers.py +138 -0
  354. bootstack/widgets/_impl/composites/textarea/extensions/pygments_highlighter.py +312 -0
  355. bootstack/widgets/_impl/composites/textarea/extensions/smart_indent.py +177 -0
  356. bootstack/widgets/_impl/composites/textarea/filter.py +171 -0
  357. bootstack/widgets/_impl/composites/textarea/search_overlay.py +459 -0
  358. bootstack/widgets/_impl/composites/textarea/sidebar.py +88 -0
  359. bootstack/widgets/_impl/composites/textarea/style_registry.py +178 -0
  360. bootstack/widgets/_impl/composites/textarea/textarea.py +606 -0
  361. bootstack/widgets/_impl/composites/textarea/undo.py +217 -0
  362. bootstack/widgets/_impl/composites/textentry.py +57 -0
  363. bootstack/widgets/_impl/composites/timeentry.py +176 -0
  364. bootstack/widgets/_impl/composites/toast.py +390 -0
  365. bootstack/widgets/_impl/composites/toast_stack.py +156 -0
  366. bootstack/widgets/_impl/composites/togglegroup.py +418 -0
  367. bootstack/widgets/_impl/composites/toolbar.py +608 -0
  368. bootstack/widgets/_impl/composites/tooltip.py +491 -0
  369. bootstack/widgets/_impl/composites/tree/__init__.py +7 -0
  370. bootstack/widgets/_impl/composites/tree/source_binding.py +136 -0
  371. bootstack/widgets/_impl/composites/tree/treeitem.py +393 -0
  372. bootstack/widgets/_impl/composites/tree/treenode.py +174 -0
  373. bootstack/widgets/_impl/composites/tree/treeview.py +841 -0
  374. bootstack/widgets/_impl/mixins/__init__.py +24 -0
  375. bootstack/widgets/_impl/mixins/configure_mixin.py +216 -0
  376. bootstack/widgets/_impl/mixins/entry_mixin.py +134 -0
  377. bootstack/widgets/_impl/mixins/font_mixin.py +368 -0
  378. bootstack/widgets/_impl/mixins/icon_mixin.py +61 -0
  379. bootstack/widgets/_impl/mixins/localization_mixin.py +253 -0
  380. bootstack/widgets/_impl/mixins/signal_mixin.py +268 -0
  381. bootstack/widgets/_impl/mixins/validation_mixin.py +226 -0
  382. bootstack/widgets/_impl/primitives/__init__.py +49 -0
  383. bootstack/widgets/_impl/primitives/_menubutton.py +107 -0
  384. bootstack/widgets/_impl/primitives/badge.py +45 -0
  385. bootstack/widgets/_impl/primitives/button.py +76 -0
  386. bootstack/widgets/_impl/primitives/card.py +45 -0
  387. bootstack/widgets/_impl/primitives/checkbutton.py +124 -0
  388. bootstack/widgets/_impl/primitives/checktoggle.py +62 -0
  389. bootstack/widgets/_impl/primitives/combobox.py +156 -0
  390. bootstack/widgets/_impl/primitives/entry.py +87 -0
  391. bootstack/widgets/_impl/primitives/flexframe.py +448 -0
  392. bootstack/widgets/_impl/primitives/frame.py +185 -0
  393. bootstack/widgets/_impl/primitives/gridframe.py +546 -0
  394. bootstack/widgets/_impl/primitives/label.py +84 -0
  395. bootstack/widgets/_impl/primitives/labelframe.py +54 -0
  396. bootstack/widgets/_impl/primitives/optionmenu.py +387 -0
  397. bootstack/widgets/_impl/primitives/packframe.py +227 -0
  398. bootstack/widgets/_impl/primitives/panedwindow.py +45 -0
  399. bootstack/widgets/_impl/primitives/progressbar.py +83 -0
  400. bootstack/widgets/_impl/primitives/radiobutton.py +115 -0
  401. bootstack/widgets/_impl/primitives/radiotoggle.py +54 -0
  402. bootstack/widgets/_impl/primitives/scrollbar.py +42 -0
  403. bootstack/widgets/_impl/primitives/separator.py +43 -0
  404. bootstack/widgets/_impl/primitives/sizegrip.py +33 -0
  405. bootstack/widgets/_impl/primitives/spinbox.py +95 -0
  406. bootstack/widgets/_impl/primitives/switch.py +44 -0
  407. bootstack/widgets/_impl/primitives/treeview.py +69 -0
  408. bootstack/widgets/app.py +371 -0
  409. bootstack/widgets/appshell.py +1179 -0
  410. bootstack/widgets/avatar.py +140 -0
  411. bootstack/widgets/boolean_controls.py +455 -0
  412. bootstack/widgets/button.py +224 -0
  413. bootstack/widgets/buttongroup.py +239 -0
  414. bootstack/widgets/calendar.py +195 -0
  415. bootstack/widgets/card.py +159 -0
  416. bootstack/widgets/carousel.py +241 -0
  417. bootstack/widgets/chart.py +302 -0
  418. bootstack/widgets/codeeditor.py +675 -0
  419. bootstack/widgets/contextmenu.py +371 -0
  420. bootstack/widgets/datatable.py +688 -0
  421. bootstack/widgets/datefield.py +395 -0
  422. bootstack/widgets/divider.py +60 -0
  423. bootstack/widgets/expander.py +579 -0
  424. bootstack/widgets/form.py +200 -0
  425. bootstack/widgets/gallery.py +250 -0
  426. bootstack/widgets/gauge.py +168 -0
  427. bootstack/widgets/grid.py +121 -0
  428. bootstack/widgets/groupbox.py +162 -0
  429. bootstack/widgets/label.py +229 -0
  430. bootstack/widgets/listview.py +354 -0
  431. bootstack/widgets/menubutton.py +419 -0
  432. bootstack/widgets/numberfield.py +431 -0
  433. bootstack/widgets/pagestack.py +345 -0
  434. bootstack/widgets/passwordfield.py +383 -0
  435. bootstack/widgets/pathfield.py +454 -0
  436. bootstack/widgets/picture.py +251 -0
  437. bootstack/widgets/progressbar.py +106 -0
  438. bootstack/widgets/radio_variants.py +271 -0
  439. bootstack/widgets/radiogroup.py +228 -0
  440. bootstack/widgets/scrollbar.py +64 -0
  441. bootstack/widgets/scrollview.py +186 -0
  442. bootstack/widgets/select.py +302 -0
  443. bootstack/widgets/selectbutton.py +182 -0
  444. bootstack/widgets/sidebar_toggle.py +130 -0
  445. bootstack/widgets/sizegrip.py +42 -0
  446. bootstack/widgets/slider.py +408 -0
  447. bootstack/widgets/spinbox.py +147 -0
  448. bootstack/widgets/spinnerfield.py +413 -0
  449. bootstack/widgets/splash.py +367 -0
  450. bootstack/widgets/splitview.py +482 -0
  451. bootstack/widgets/stacks.py +249 -0
  452. bootstack/widgets/statusbar.py +189 -0
  453. bootstack/widgets/tabs.py +425 -0
  454. bootstack/widgets/textarea.py +459 -0
  455. bootstack/widgets/textfield.py +410 -0
  456. bootstack/widgets/theme_toggle.py +92 -0
  457. bootstack/widgets/timefield.py +340 -0
  458. bootstack/widgets/toast.py +314 -0
  459. bootstack/widgets/togglegroup.py +231 -0
  460. bootstack/widgets/toolbar.py +347 -0
  461. bootstack/widgets/tooltip.py +94 -0
  462. bootstack/widgets/tree.py +648 -0
  463. bootstack/widgets/types.py +305 -0
  464. bootstack/widgets/window.py +307 -0
  465. bootstack-0.1.0.dist-info/METADATA +301 -0
  466. bootstack-0.1.0.dist-info/RECORD +471 -0
  467. bootstack-0.1.0.dist-info/WHEEL +5 -0
  468. bootstack-0.1.0.dist-info/entry_points.txt +2 -0
  469. bootstack-0.1.0.dist-info/licenses/LICENSE +21 -0
  470. bootstack-0.1.0.dist-info/licenses/NOTICE +10 -0
  471. bootstack-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,431 @@
1
+ from __future__ import annotations
2
+
3
+ import tkinter
4
+ from typing import overload, Any, Callable, TYPE_CHECKING
5
+
6
+ from bootstack.widgets._impl.composites.numericentry import NumericEntry as _InternalNumericEntry
7
+ from bootstack.widgets._core.base import PublicWidgetBase, adapt_handler
8
+ from bootstack.widgets._core.events import resolve_event, register_widget_events
9
+ from bootstack.widgets._core.field_mixin import FieldAddonMixin, ValueSignalMixin
10
+ from bootstack.events import ChangeEvent, InputEvent, Subscription, ValidationEvent
11
+ from bootstack.streams import Stream
12
+ from bootstack.widgets.textfield import _INNER_ENTRY_SEQUENCES
13
+ from bootstack.widgets.types import AccentToken, Event, Justify, WidgetDensity
14
+
15
+ if TYPE_CHECKING:
16
+ from bootstack.signals import Signal
17
+
18
+ def _as_number(raw: Any) -> int | float | None:
19
+ """Coerce a field's stored value to a number, regardless of representation.
20
+
21
+ The numeric field stores its value as text in some code paths; the public
22
+ contract is that `value` (and the bound `signal`) is always a number.
23
+ """
24
+ if raw is None or raw == "":
25
+ return None
26
+ if isinstance(raw, (int, float)):
27
+ return raw
28
+ try:
29
+ f = float(raw)
30
+ except (TypeError, ValueError):
31
+ return None
32
+ return int(f) if f.is_integer() else f
33
+
34
+
35
+ _NUMBER_FIELD_EVENTS: dict[str, str] = {
36
+ "input": "<<Input>>",
37
+ "change": "<<Change>>",
38
+ "valid": "<<Valid>>",
39
+ "invalid": "<<Invalid>>",
40
+ "validate": "<<Validate>>",
41
+ "submit": "<Return>",
42
+ "focus": "<FocusIn>",
43
+ "blur": "<FocusOut>",
44
+ }
45
+
46
+
47
+ class NumberField(ValueSignalMixin, FieldAddonMixin, PublicWidgetBase):
48
+ """A numeric input field with optional stepper buttons.
49
+
50
+ Accepts integer or float values. Up/Down arrow keys and the mouse wheel
51
+ step the value by `step`. The field validates that the typed value is
52
+ numeric and within `min_value`/`max_value` bounds.
53
+
54
+ The initial value is the first positional argument. All options are
55
+ keyword-only.
56
+
57
+ Args:
58
+ value: Initial numeric value. Defaults to `0`.
59
+ signal: Reactive `Signal` two-way bound to the field's numeric value.
60
+ Carries the parsed number (`int`/`float`), not the text. Use a
61
+ float-typed signal — `Signal(0.0)` — when the field accepts
62
+ fractional values, so they bind cleanly; an `int`-typed `Signal(0)`
63
+ holds whole numbers only. When given, it seeds the initial value.
64
+ This is the usual way to bind a number field.
65
+ label: Label displayed above the field.
66
+ message: Hint or helper text displayed below the field.
67
+ min_value: Minimum allowed value (inclusive). No lower bound if omitted.
68
+ max_value: Maximum allowed value (inclusive). No upper bound if omitted.
69
+ step: Amount added or subtracted per increment/decrement. Defaults to
70
+ `1`.
71
+ wrap: If `True`, stepping past `max_value` wraps to `min_value` and
72
+ vice versa (requires both bounds set). Defaults to `False`.
73
+ show_steppers: If `False`, hides the +/− stepper buttons. Defaults
74
+ to `True`.
75
+ required: If `True`, marks the field as required and prevents empty
76
+ submission.
77
+ read_only: If `True`, value is visible and selectable but not
78
+ editable.
79
+ disabled: If `True`, field is fully non-interactive and dimmed.
80
+ width: Width in character units.
81
+ justify: Text alignment inside the entry. Default `'left'`.
82
+ font: Semantic font token (e.g. `'body'`, `'code'`). See
83
+ :doc:`/reference/typography`.
84
+ value_format: Format applied when displaying the value — a named preset
85
+ (e.g. `'decimal'`, `'currency'`, `'percent'`) or a custom pattern
86
+ (e.g. `'#,##0.00'`). Requires localization enabled. See
87
+ :ref:`format specs <value-formats>`.
88
+ accent: Accent token applied to the focus ring.
89
+ density: Padding density.
90
+ parent: Explicit parent widget. If omitted, the current context-stack
91
+ container is used.
92
+ **kwargs: Layout placement options applied by the parent container —
93
+ `fill`, `expand`, `anchor`, `margin`, `row`, `column`, `sticky`.
94
+ See :doc:`/tasks/layout`.
95
+ """
96
+
97
+ _VALIDATION_KIND = "number"
98
+
99
+ def __init__(
100
+ self,
101
+ value: int | float = 0,
102
+ *,
103
+ signal: "Signal | None" = None,
104
+ value_format: str | None = None,
105
+ label: str | None = None,
106
+ message: str | None = None,
107
+ min_value: int | float | None = None,
108
+ max_value: int | float | None = None,
109
+ step: int | float = 1,
110
+ wrap: bool = False,
111
+ show_steppers: bool = True,
112
+ required: bool = False,
113
+ read_only: bool = False,
114
+ disabled: bool = False,
115
+ width: int | None = None,
116
+ justify: Justify | None = None,
117
+ font: str | None = None,
118
+ accent: AccentToken | str | None = None,
119
+ density: WidgetDensity | None = None,
120
+ parent: Any = None,
121
+ **kwargs: Any,
122
+ ) -> None:
123
+ self._parent = self._resolve_parent(parent)
124
+ layout_kw = self._split_layout_kwargs(kwargs)
125
+ if "textsignal" in kwargs:
126
+ raise TypeError(
127
+ "NumberField does not accept 'textsignal=' — a number field binds "
128
+ "its numeric value. Use signal= with a number-typed Signal "
129
+ "(e.g. Signal(0) or Signal(0.0))."
130
+ )
131
+ tk_master = self._parent._child_master() if self._parent else None
132
+
133
+ internal_kwargs: dict[str, Any] = {
134
+ "value": value,
135
+ "increment": step,
136
+ "show_spin_buttons": show_steppers,
137
+ }
138
+ if wrap:
139
+ internal_kwargs["wrap"] = True
140
+ if value_format is not None:
141
+ internal_kwargs["value_format"] = value_format
142
+ if label is not None:
143
+ internal_kwargs["label"] = label
144
+ if message is not None:
145
+ internal_kwargs["message"] = message
146
+ if min_value is not None:
147
+ internal_kwargs["minvalue"] = min_value
148
+ if max_value is not None:
149
+ internal_kwargs["maxvalue"] = max_value
150
+ if required:
151
+ internal_kwargs["required"] = True
152
+ if disabled:
153
+ internal_kwargs["state"] = "disabled"
154
+ elif read_only:
155
+ internal_kwargs["state"] = "readonly"
156
+ if width is not None:
157
+ internal_kwargs["width"] = width
158
+ if justify is not None:
159
+ internal_kwargs["justify"] = justify
160
+ if font is not None:
161
+ internal_kwargs["font"] = font
162
+ if accent is not None:
163
+ internal_kwargs["accent"] = accent
164
+ if density is not None:
165
+ internal_kwargs["density"] = density
166
+
167
+ self._internal = _InternalNumericEntry(tk_master, **internal_kwargs)
168
+ self._attach_to_parent(layout_kw)
169
+
170
+ if signal is not None:
171
+ self._bind_value_signal(signal)
172
+
173
+ # ----- Event routing -----
174
+
175
+ def _entry_widget(self) -> tkinter.Misc:
176
+ return self._internal._entry
177
+
178
+ @overload
179
+ def on(self, event: str) -> Stream: ...
180
+ @overload
181
+ def on(self, event: str, handler: Callable[[Event], Any]) -> Subscription: ...
182
+ def on(self, event: str, handler: Callable[[Event], Any] | None = None) -> Stream | Subscription:
183
+ """Register a callback for an event by name.
184
+
185
+ A generic, string-keyed escape hatch — prefer the typed `on_*`
186
+ shorthands (e.g. `on_change`), which carry the precise payload type.
187
+ Called with no handler, returns a composable `Stream`; with a handler,
188
+ binds it and returns a `Subscription`.
189
+
190
+ Args:
191
+ event: Event name (for example `'change'` or `'focus'`).
192
+ handler: Called with the event payload. Omit to get a composable
193
+ :class:`~bootstack.streams.Stream` instead.
194
+
195
+ Returns:
196
+ A cancellable :class:`~bootstack.events.Subscription` when a handler
197
+ is given, otherwise a :class:`~bootstack.streams.Stream`.
198
+ """
199
+ sequence = resolve_event(self, str(event))
200
+ target = self._entry_widget() if sequence in _INNER_ENTRY_SEQUENCES else self._internal
201
+ if handler is None:
202
+ def _source(h):
203
+ t = self._entry_widget() if sequence in _INNER_ENTRY_SEQUENCES else self._internal
204
+ bid = t.bind(sequence, adapt_handler(h), add="+")
205
+ return Subscription(t, sequence, bid)
206
+ return Stream(self._internal, _source=_source)
207
+ bid = target.bind(sequence, adapt_handler(handler), add="+")
208
+ return Subscription(target, sequence, bid)
209
+
210
+ # ----- Properties -----
211
+
212
+ @property
213
+ def value(self) -> int | float | None:
214
+ """The current numeric value, or `None` if the field is empty."""
215
+ return _as_number(self._internal.value)
216
+
217
+ @value.setter
218
+ def value(self, v: int | float) -> None:
219
+ self._internal.value = v
220
+ self._sync_value_set(v)
221
+
222
+ @property
223
+ def read_only(self) -> bool:
224
+ """Whether the field is visible but not editable."""
225
+ return str(self._entry_widget().cget("state")) == "readonly"
226
+
227
+ @read_only.setter
228
+ def read_only(self, v: bool) -> None:
229
+ self._internal.configure(state="readonly" if v else "normal")
230
+
231
+ @property
232
+ def disabled(self) -> bool:
233
+ """Whether the field is fully non-interactive."""
234
+ return str(self._entry_widget().cget("state")) == "disabled"
235
+
236
+ @disabled.setter
237
+ def disabled(self, v: bool) -> None:
238
+ self._internal.configure(state="disabled" if v else "normal")
239
+
240
+ # ----- Methods -----
241
+
242
+ def increment(self, steps: int = 1) -> None:
243
+ """Increment the value by `steps` × step size.
244
+
245
+ Args:
246
+ steps: Number of steps to increment. Defaults to `1`.
247
+ """
248
+ self._internal.step(steps)
249
+
250
+ def decrement(self, steps: int = 1) -> None:
251
+ """Decrement the value by `steps` × step size.
252
+
253
+ Args:
254
+ steps: Number of steps to decrement. Defaults to `1`.
255
+ """
256
+ self._internal.step(-steps)
257
+
258
+ def validate(self) -> bool:
259
+ """Run validation rules against the current value.
260
+
261
+ Returns:
262
+ `True` if all rules pass, `False` otherwise.
263
+ """
264
+ entry = self._internal._entry
265
+ return entry.validate(entry._get_validation_value(), trigger="manual")
266
+
267
+ def focus(self) -> None:
268
+ """Give keyboard focus to this field."""
269
+ self._entry_widget().focus_set()
270
+
271
+ def clear(self) -> None:
272
+ """Clear the field, leaving it empty.
273
+
274
+ The value becomes `None` (an empty field), not `0` — so a cleared
275
+ required field still reads as blank.
276
+ """
277
+ self._internal.value = ""
278
+
279
+ def select_all(self) -> None:
280
+ """Select all text in the field."""
281
+ self._entry_widget().selection_range(0, "end")
282
+
283
+ def select_range(self, start: int, end: int) -> None:
284
+ """Select text between `start` and `end` character positions.
285
+
286
+ Args:
287
+ start: Start index (0-based, inclusive).
288
+ end: End index (exclusive).
289
+ """
290
+ self._entry_widget().selection_range(start, end)
291
+
292
+ # ----- Event shorthands -----
293
+
294
+ @overload
295
+ def on_input(self) -> Stream: ...
296
+ @overload
297
+ def on_input(self, handler: Callable[[InputEvent], Any]) -> Subscription: ...
298
+ def on_input(self, handler: Callable[[InputEvent], Any] | None = None) -> Stream | Subscription:
299
+ """Register a callback fired on every keystroke.
300
+
301
+ Args:
302
+ handler: Called with an :class:`~bootstack.events.InputEvent`. Omit to
303
+ get a composable :class:`~bootstack.streams.Stream` instead.
304
+
305
+ Returns:
306
+ A cancellable :class:`~bootstack.events.Subscription` when a
307
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
308
+ """
309
+ return self.on("input", handler)
310
+
311
+ @overload
312
+ def on_change(self) -> Stream: ...
313
+ @overload
314
+ def on_change(self, handler: Callable[[ChangeEvent], Any]) -> Subscription: ...
315
+ def on_change(self, handler: Callable[[ChangeEvent], Any] | None = None) -> Stream | Subscription:
316
+ """Register a callback fired when the value is committed.
317
+
318
+ Args:
319
+ handler: Called with a :class:`~bootstack.events.ChangeEvent`. Omit to
320
+ get a composable :class:`~bootstack.streams.Stream` instead.
321
+
322
+ Returns:
323
+ A cancellable :class:`~bootstack.events.Subscription` when a
324
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
325
+ """
326
+ return self.on("change", handler)
327
+
328
+ @overload
329
+ def on_submit(self) -> Stream: ...
330
+ @overload
331
+ def on_submit(self, handler: Callable[[Event], Any]) -> Subscription: ...
332
+ def on_submit(self, handler: Callable[[Event], Any] | None = None) -> Stream | Subscription:
333
+ """Register a callback fired when the user presses Enter.
334
+
335
+ Args:
336
+ handler: Called with an :class:`~bootstack.events.Event`. Omit to
337
+ get a composable :class:`~bootstack.streams.Stream` instead.
338
+
339
+ Returns:
340
+ A cancellable :class:`~bootstack.events.Subscription` when a
341
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
342
+ """
343
+ return self.on("submit", handler)
344
+
345
+ @overload
346
+ def on_focus(self) -> Stream: ...
347
+ @overload
348
+ def on_focus(self, handler: Callable[[Event], Any]) -> Subscription: ...
349
+ def on_focus(self, handler: Callable[[Event], Any] | None = None) -> Stream | Subscription:
350
+ """Register a callback fired when the field gains focus.
351
+
352
+ Args:
353
+ handler: Called with an :class:`~bootstack.events.Event`. Omit to
354
+ get a composable :class:`~bootstack.streams.Stream` instead.
355
+
356
+ Returns:
357
+ A cancellable :class:`~bootstack.events.Subscription` when a
358
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
359
+ """
360
+ return self.on("focus", handler)
361
+
362
+ @overload
363
+ def on_blur(self) -> Stream: ...
364
+ @overload
365
+ def on_blur(self, handler: Callable[[Event], Any]) -> Subscription: ...
366
+ def on_blur(self, handler: Callable[[Event], Any] | None = None) -> Stream | Subscription:
367
+ """Register a callback fired when the field loses focus.
368
+
369
+ Args:
370
+ handler: Called with an :class:`~bootstack.events.Event`. Omit to
371
+ get a composable :class:`~bootstack.streams.Stream` instead.
372
+
373
+ Returns:
374
+ A cancellable :class:`~bootstack.events.Subscription` when a
375
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
376
+ """
377
+ return self.on("blur", handler)
378
+
379
+ @overload
380
+ def on_valid(self) -> Stream: ...
381
+ @overload
382
+ def on_valid(self, handler: Callable[[ValidationEvent], Any]) -> Subscription: ...
383
+ def on_valid(self, handler: Callable[[ValidationEvent], Any] | None = None) -> Stream | Subscription:
384
+ """Register a callback fired when validation passes.
385
+
386
+ Args:
387
+ handler: Called with a :class:`~bootstack.events.ValidationEvent`. Omit to
388
+ get a composable :class:`~bootstack.streams.Stream` instead.
389
+
390
+ Returns:
391
+ A cancellable :class:`~bootstack.events.Subscription` when a
392
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
393
+ """
394
+ return self.on("valid", handler)
395
+
396
+ @overload
397
+ def on_invalid(self) -> Stream: ...
398
+ @overload
399
+ def on_invalid(self, handler: Callable[[ValidationEvent], Any]) -> Subscription: ...
400
+ def on_invalid(self, handler: Callable[[ValidationEvent], Any] | None = None) -> Stream | Subscription:
401
+ """Register a callback fired when validation fails.
402
+
403
+ Args:
404
+ handler: Called with a :class:`~bootstack.events.ValidationEvent`. Omit to
405
+ get a composable :class:`~bootstack.streams.Stream` instead.
406
+
407
+ Returns:
408
+ A cancellable :class:`~bootstack.events.Subscription` when a
409
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
410
+ """
411
+ return self.on("invalid", handler)
412
+
413
+ @overload
414
+ def on_validate(self) -> Stream: ...
415
+ @overload
416
+ def on_validate(self, handler: Callable[[ValidationEvent], Any]) -> Subscription: ...
417
+ def on_validate(self, handler: Callable[[ValidationEvent], Any] | None = None) -> Stream | Subscription:
418
+ """Register a callback fired after any validation run.
419
+
420
+ Args:
421
+ handler: Called with a :class:`~bootstack.events.ValidationEvent`. Omit to
422
+ get a composable :class:`~bootstack.streams.Stream` instead.
423
+
424
+ Returns:
425
+ A cancellable :class:`~bootstack.events.Subscription` when a
426
+ handler is given, otherwise a :class:`~bootstack.streams.Stream`.
427
+ """
428
+ return self.on("validate", handler)
429
+
430
+
431
+ register_widget_events(NumberField, _NUMBER_FIELD_EVENTS)