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,434 @@
1
+ """Spinner entry widget with value management and validation.
2
+
3
+ This module provides a spinner entry widget that extends Spinbox with
4
+ value management features like parsing, formatting, and event handling for
5
+ both text and numeric values.
6
+ """
7
+
8
+ from tkinter import Event, TclError
9
+ from typing import Any, Callable
10
+
11
+ from bootstack.events import ChangeEvent, InputEvent
12
+ from bootstack.i18n import MessageCatalog, IntlFormatter
13
+ from bootstack.widgets._impl.primitives.spinbox import Spinbox
14
+ from bootstack.widgets._impl.mixins import ValidationMixin
15
+ from bootstack.widgets._impl.mixins.configure_mixin import configure_delegate
16
+
17
+
18
+ class SpinnerEntryPart(ValidationMixin, Spinbox):
19
+ """Spinner widget with internationalization and value management.
20
+
21
+ This widget extends Spinbox to provide the same value/text management
22
+ pattern as TextEntryPart and NumberEntryPart. It can safely handle both
23
+ text and numeric values with optional formatting.
24
+
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ master=None,
30
+ *,
31
+ value: int | float | str = '',
32
+ value_format: str = None,
33
+ values: list[str] = None,
34
+ from_: int | float = None,
35
+ to: int | float = None,
36
+ increment: int | float = 1,
37
+ wrap: bool = False,
38
+ initial_focus: bool = False,
39
+ allow_blank: bool = True,
40
+ **kwargs
41
+ ):
42
+ """Initialize a SpinnerEntryPart widget.
43
+
44
+ Creates a spinner widget that separates display text from the parsed value.
45
+ The widget defers parsing and formatting until the user commits the value
46
+ by pressing Enter or moving focus away from the widget.
47
+
48
+ Args:
49
+ master: Parent widget. If None, uses the default root window.
50
+ value: Initial value to display and parse. Can be a string, int, or float.
51
+ Default is empty string.
52
+ value_format: ICU format pattern for parsing and formatting the value.
53
+ Common patterns:
54
+ - Numbers: '#,##0.00' (decimal with thousands separator)
55
+ - Currency: '¤#,##0.00' (currency symbol with amount)
56
+ - Dates: 'yyyy-MM-dd' (ISO date format)
57
+ - Percent: '#,##0.00%' (percentage)
58
+ If None, value is treated as plain text (no parsing/formatting).
59
+ values: List of valid string values for the spinner. If provided,
60
+ the spinner cycles through these values. Mutually exclusive with
61
+ from_/to/increment.
62
+ from_: Minimum numeric value (inclusive). Only used for numeric spinners.
63
+ to: Maximum numeric value (inclusive). Only used for numeric spinners.
64
+ increment: Step size for increment/decrement. Default is 1.
65
+ wrap: If True, values wrap around at boundaries. Default is False.
66
+ initial_focus: If True, widget receives focus when created. Default is False.
67
+ allow_blank: If True, empty input is parsed as None. If False, empty
68
+ input preserves the previous value. Default is True.
69
+ **kwargs: Additional keyword arguments passed to SpinnerPart.
70
+
71
+ Note:
72
+ The widget automatically subscribes to text changes and sets up
73
+ event handlers for <FocusIn>, <FocusOut>, and <Return>.
74
+ """
75
+ # Store configuration
76
+ self._value_format = value_format
77
+ self._allow_blank = allow_blank
78
+ self._on_input_fid = None
79
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
80
+
81
+ # Configure Spinbox parameters
82
+ spinbox_kwargs = {}
83
+ if values is not None:
84
+ spinbox_kwargs['values'] = values
85
+ if from_ is not None:
86
+ spinbox_kwargs['from_'] = from_
87
+ if to is not None:
88
+ spinbox_kwargs['to'] = to
89
+ if increment is not None:
90
+ spinbox_kwargs['increment'] = increment
91
+ spinbox_kwargs['wrap'] = wrap
92
+
93
+ # Merge with user kwargs and set ttk_class/variant for field styling
94
+ spinbox_kwargs.update(kwargs)
95
+ spinbox_kwargs.setdefault('ttk_class', 'TField')
96
+ spinbox_kwargs.setdefault('variant', 'spinner')
97
+
98
+ # Initialize base Spinbox with ValidationMixin
99
+ super().__init__(master, **spinbox_kwargs)
100
+
101
+ # Set the initial display value
102
+ if isinstance(value, (int, float)):
103
+ initial_display = str(value)
104
+ else:
105
+ initial_display = value or self.textsignal() or ''
106
+
107
+ # Parse initial value if format is specified
108
+ if value_format is not None:
109
+ initial_value = self._parse_or_none(initial_display)
110
+ else:
111
+ initial_value = initial_display or ''
112
+
113
+ self._value = initial_value
114
+ self._prev_changed_value = initial_value
115
+
116
+ # Normalize initial display if we already have a parsed value
117
+ if self._value is not None:
118
+ formatted_text = self._format_value(self._value)
119
+ self.textsignal.set(formatted_text)
120
+ else:
121
+ self.textsignal.set('')
122
+
123
+ # Track last text emitted for input events
124
+ self._prev_change_text = self.textsignal()
125
+
126
+ # Subscribe to text changes
127
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
128
+
129
+ # Commit on focus out / enter
130
+ self.bind('<FocusIn>', self._store_prev_value, add=True)
131
+ self.bind('<FocusOut>', self._handle_focus_out, add=True)
132
+ self.bind('<Return>', self._handle_return, add=True)
133
+ self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
134
+
135
+ # Set initial focus
136
+ if initial_focus:
137
+ self.focus()
138
+
139
+ def _store_prev_value(self, _: Any):
140
+ """Store current value on focus-in to detect changes later."""
141
+ self._prev_changed_value = self._value
142
+
143
+ def _handle_focus_out(self, _):
144
+ """Commit value and check for changes when focus leaves the widget."""
145
+ self.commit()
146
+ self._check_if_changed()
147
+
148
+ def _handle_return(self, _):
149
+ """Commit value and check for changes when Return key is pressed."""
150
+ self.commit()
151
+ self._check_if_changed()
152
+
153
+ def _handle_change(self, event):
154
+ """Emit <<Input>> event on every text change without parsing."""
155
+ text = self.textsignal()
156
+ if text == self._prev_change_text:
157
+ return
158
+
159
+ self._prev_change_text = text
160
+ self.event_generate('<<Input>>', data=InputEvent(text=text))
161
+
162
+ def _check_if_changed(self):
163
+ """Emit <<Change>> event if parsed value changed since focus-in."""
164
+ if self._value != self._prev_changed_value:
165
+ data = ChangeEvent(
166
+ value=self._value,
167
+ prev_value=self._prev_changed_value,
168
+ text=self.textsignal(),
169
+ )
170
+ self.event_generate('<<Change>>', data=data)
171
+ self._prev_changed_value = self._value
172
+
173
+ def step(self, n: int = 1) -> None:
174
+ """Step the value by `n` positions (negative steps down).
175
+
176
+ A thin abstraction over the native spinbox: fires its own
177
+ `<<Increment>>` / `<<Decrement>>` virtual events `n` times (the same
178
+ events its arrow buttons and Up/Down keys generate), so the spinbox
179
+ handles bounds, wrapping, and the text-vs-numeric mode itself. The
180
+ stepped text is then committed so the parsed value and a `<<Change>>`
181
+ event reflect the step. No-op when disabled or read-only.
182
+
183
+ Args:
184
+ n: Number of steps. Positive steps up, negative steps down.
185
+ """
186
+ states = self.state()
187
+ if 'disabled' in states or 'readonly' in states:
188
+ return
189
+
190
+ prev_value = self._value
191
+ event = '<<Increment>>' if n >= 0 else '<<Decrement>>'
192
+
193
+ # A programmatic step is a value change, not typed input — silence the
194
+ # per-keystroke <<Input>> while the native spinbox rewrites the display
195
+ # text, then re-subscribe (mirrors NumberEntryPart's silenced stepping).
196
+ fid = getattr(self, '_on_input_fid', None)
197
+ if fid:
198
+ try:
199
+ fid.cancel()
200
+ except TclError:
201
+ pass
202
+ self._on_input_fid = None
203
+ try:
204
+ for _ in range(abs(n)):
205
+ # `when='now'` delivers synchronously so commit() below sees
206
+ # the stepped text immediately.
207
+ self.event_generate(event, when='now')
208
+ self.commit()
209
+ finally:
210
+ self._prev_change_text = self.textsignal()
211
+ if fid:
212
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
213
+
214
+ if self._value != prev_value:
215
+ self._prev_changed_value = self._value
216
+ self.event_generate('<<Change>>', data=ChangeEvent(
217
+ value=self._value,
218
+ prev_value=prev_value,
219
+ text=self.textsignal(),
220
+ ))
221
+
222
+ def _parse_or_none(self, s: str):
223
+ """Parse string using value_format, returning None on empty/invalid input."""
224
+ # If a non-string is passed (e.g., datetime/date), assume it's already parsed
225
+ if not isinstance(s, str):
226
+ return s
227
+ s2 = (s or '').strip()
228
+ if not s2:
229
+ return None
230
+ try:
231
+ if self._value_format is None:
232
+ return s2
233
+ return self._fmt.parse(s2, self._value_format)
234
+ except ValueError:
235
+ return None
236
+
237
+ def _format_value(self, value: Any) -> str:
238
+ """Format a value for display using value_format.
239
+
240
+ Args:
241
+ value: The value to format (can be string, int, float, etc.)
242
+
243
+ Returns:
244
+ Formatted string representation of the value
245
+ """
246
+ if value is None:
247
+ return ''
248
+
249
+ # If no format specified, just convert to string
250
+ if self._value_format is None:
251
+ return str(value)
252
+
253
+ # IntlFormatter.format() expects numeric values for numeric formats
254
+ try:
255
+ return self._fmt.format(value, self._value_format)
256
+ except (ValueError, TypeError):
257
+ # If formatting fails, return string representation
258
+ return str(value)
259
+
260
+ def on_input(self, callback: Callable[[Any], Any]) -> str:
261
+ """Bind callback to <<Input>> event (fires on every keystroke).
262
+
263
+ Args:
264
+ callback: Function receiving event.data = {"text": str}
265
+
266
+ Returns:
267
+ Binding identifier for use with off_input()
268
+ """
269
+ return self.bind('<<Input>>', callback, add=True)
270
+
271
+ def off_input(self, bind_id: str | None = None):
272
+ """Remove callback from <<Input>> event."""
273
+ self.unbind('<<Input>>', bind_id)
274
+
275
+ def on_enter(self, callback: Callable[[Any], Any]) -> str:
276
+ """Bind callback to <Return> event.
277
+
278
+ Args:
279
+ callback: Function receiving event.data = {"value": Any, "text": str}
280
+
281
+ Returns:
282
+ Binding identifier for use with off_enter()
283
+ """
284
+ def enrich_callback(event: Event) -> None:
285
+ data = {"value": self._value, "text": self.textsignal()}
286
+ event.data = data
287
+ return callback(event)
288
+
289
+ return self.bind('<Return>', enrich_callback, add=True)
290
+
291
+ def off_enter(self, bind_id: str | None = None):
292
+ """Remove callback from <Return> event."""
293
+ self.unbind('<Return>', bind_id)
294
+
295
+ def on_changed(self, callback: Callable[[Any], Any]) -> str:
296
+ """Bind callback to <<Change>> event (fires when value changes on commit).
297
+
298
+ Args:
299
+ callback: Function receiving event.data = {"value": Any, "prev_value": Any, "text": str}
300
+
301
+ Returns:
302
+ Binding identifier for use with off_changed()
303
+ """
304
+ return self.bind("<<Change>>", callback)
305
+
306
+ def off_changed(self, bind_id: str | None = None):
307
+ """Remove callback from <<Change>> event."""
308
+ self.unbind('<<Change>>', bind_id)
309
+
310
+ def value(self, value=None):
311
+ """Get or set the parsed/committed value.
312
+
313
+ Args:
314
+ value: If provided, sets the display text and internal value with formatting
315
+
316
+ Returns:
317
+ Current parsed value if no argument provided, None otherwise
318
+ """
319
+ if value is None:
320
+ return self._value
321
+ else:
322
+ # Store the value and format it for display
323
+ if isinstance(value, (int, float)):
324
+ value_str = str(value)
325
+ else:
326
+ value_str = str(value) if value is not None else ''
327
+
328
+ # Parse the value if format is specified
329
+ if self._value_format is not None and value_str:
330
+ self._value = self._parse_or_none(value_str)
331
+ else:
332
+ self._value = value_str if value_str else None
333
+
334
+ # Format and display
335
+ formatted_text = self._format_value(self._value)
336
+ self.textsignal.set(formatted_text)
337
+ return None
338
+
339
+ def text(self, value=None):
340
+ """Get or set the raw display text without committing.
341
+
342
+ Args:
343
+ value: If provided, sets the display text without parsing
344
+
345
+ Returns:
346
+ Current display text if no argument provided, None otherwise
347
+ """
348
+ if value is None:
349
+ return self.textsignal()
350
+ else:
351
+ self.textsignal.set(value)
352
+ return None
353
+
354
+ def commit(self):
355
+ """Parse display text, update value, and normalize display (called on FocusOut/Return)."""
356
+ s = self.get().strip()
357
+
358
+ # Parse once
359
+ if s == '':
360
+ self._value = None if self._allow_blank else self._value
361
+ else:
362
+ try:
363
+ self._value = s if self._value_format is None else self._fmt.parse(s, self._value_format)
364
+ except ValueError:
365
+ # Keep prior value on parse failure
366
+ return
367
+
368
+ # Format the value for display
369
+ new_text = self._format_value(self._value)
370
+
371
+ if new_text != self.textsignal():
372
+ # Temporarily silence input events while normalizing text
373
+ fid = getattr(self, '_on_input_fid', None)
374
+ if fid:
375
+ try:
376
+ fid.cancel()
377
+ except TclError:
378
+ pass
379
+ self.textsignal.set(new_text)
380
+ if fid:
381
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
382
+
383
+ def _on_locale_changed(self, event=None):
384
+ """Respond to global changes in locale by updating formatter and text."""
385
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
386
+
387
+ # Reformat current value with new locale + same value_format
388
+ if self._value is not None:
389
+ formatted_text = self._format_value(self._value)
390
+ self.textsignal.set(formatted_text)
391
+
392
+ @configure_delegate('text')
393
+ def _delegate_text(self, value=None):
394
+ if value is None:
395
+ return self.text()
396
+ else:
397
+ return self.text(value)
398
+
399
+ @configure_delegate('value')
400
+ def _delegate_value(self, value=None):
401
+ if value is None:
402
+ return self.value()
403
+ else:
404
+ return self.value(value)
405
+
406
+ @configure_delegate('value_format')
407
+ def _delegate_value_format(self, value: str):
408
+ """Get or set the value format pattern and reformat display."""
409
+ if value is None:
410
+ return self._value_format
411
+
412
+ self._value_format = value
413
+ # Reformat current value with new format
414
+ if self._value is not None:
415
+ formatted_text = self._format_value(self._value)
416
+ self.textsignal.set(formatted_text)
417
+
418
+ @configure_delegate('allow_blank')
419
+ def _delegate_allow_blank(self, value: bool):
420
+ """Get or set whether blank input is allowed."""
421
+ if value is None:
422
+ return self._allow_blank
423
+
424
+ self._allow_blank = bool(value)
425
+
426
+ def destroy(self):
427
+ """Clean up signal subscriptions and destroy the widget."""
428
+ if self._on_input_fid:
429
+ try:
430
+ self._on_input_fid.cancel()
431
+ except Exception:
432
+ pass # Ignore all errors during cleanup
433
+ self._on_input_fid = None
434
+ super().destroy()