bootstack 0.1.0a1__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 (419) hide show
  1. bootstack/__init__.py +249 -0
  2. bootstack/__main__.py +5 -0
  3. bootstack/api/__init__.py +127 -0
  4. bootstack/api/app.py +30 -0
  5. bootstack/api/constants.py +3 -0
  6. bootstack/api/data.py +23 -0
  7. bootstack/api/dialogs.py +44 -0
  8. bootstack/api/i18n.py +17 -0
  9. bootstack/api/localization.py +16 -0
  10. bootstack/api/menu.py +7 -0
  11. bootstack/api/style.py +23 -0
  12. bootstack/api/utils.py +24 -0
  13. bootstack/api/widgets.py +137 -0
  14. bootstack/assets/__init__.py +24 -0
  15. bootstack/assets/bootstack-transparent.png +0 -0
  16. bootstack/assets/bootstack.ico +0 -0
  17. bootstack/assets/bootstack.png +0 -0
  18. bootstack/assets/elements/__init__.py +0 -0
  19. bootstack/assets/elements/badge-pill.png +0 -0
  20. bootstack/assets/elements/badge-square.png +0 -0
  21. bootstack/assets/elements/border.png +0 -0
  22. bootstack/assets/elements/button-compact.png +0 -0
  23. bootstack/assets/elements/button-default.png +0 -0
  24. bootstack/assets/elements/button-group-horizontal-after-compact.png +0 -0
  25. bootstack/assets/elements/button-group-horizontal-after-default.png +0 -0
  26. bootstack/assets/elements/button-group-horizontal-before-compact.png +0 -0
  27. bootstack/assets/elements/button-group-horizontal-before-default.png +0 -0
  28. bootstack/assets/elements/button-group-horizontal-center-compact.png +0 -0
  29. bootstack/assets/elements/button-group-horizontal-center-default.png +0 -0
  30. bootstack/assets/elements/button-group-vertical-after-compact.png +0 -0
  31. bootstack/assets/elements/button-group-vertical-after-default.png +0 -0
  32. bootstack/assets/elements/button-group-vertical-before-compact.png +0 -0
  33. bootstack/assets/elements/button-group-vertical-before-default.png +0 -0
  34. bootstack/assets/elements/button-group-vertical-center-compact.png +0 -0
  35. bootstack/assets/elements/button-group-vertical-center-default.png +0 -0
  36. bootstack/assets/elements/checkbox-checked.png +0 -0
  37. bootstack/assets/elements/checkbox-indeterminate.png +0 -0
  38. bootstack/assets/elements/checkbox-unchecked.png +0 -0
  39. bootstack/assets/elements/field.png +0 -0
  40. bootstack/assets/elements/input-after-compact.png +0 -0
  41. bootstack/assets/elements/input-after-default.png +0 -0
  42. bootstack/assets/elements/input-before-compact.png +0 -0
  43. bootstack/assets/elements/input-before-default.png +0 -0
  44. bootstack/assets/elements/input-compact.png +0 -0
  45. bootstack/assets/elements/input-default.png +0 -0
  46. bootstack/assets/elements/list-item-separated.png +0 -0
  47. bootstack/assets/elements/list-item.png +0 -0
  48. bootstack/assets/elements/manifest.toml +480 -0
  49. bootstack/assets/elements/menu-item.png +0 -0
  50. bootstack/assets/elements/nav-button-compact.png +0 -0
  51. bootstack/assets/elements/nav-button-default.png +0 -0
  52. bootstack/assets/elements/nav-icon-button-compact.png +0 -0
  53. bootstack/assets/elements/nav-icon-button-default.png +0 -0
  54. bootstack/assets/elements/notebook-client-border.png +0 -0
  55. bootstack/assets/elements/notebook-tab-active.png +0 -0
  56. bootstack/assets/elements/notebook-tab-bar.png +0 -0
  57. bootstack/assets/elements/notebook-tab-normal.png +0 -0
  58. bootstack/assets/elements/notebook-tab-pill.png +0 -0
  59. bootstack/assets/elements/progress-bar-horizontal-striped.png +0 -0
  60. bootstack/assets/elements/progress-bar-solid.png +0 -0
  61. bootstack/assets/elements/progress-bar-thin.png +0 -0
  62. bootstack/assets/elements/progress-bar-vertical-striped.png +0 -0
  63. bootstack/assets/elements/radio-selected.png +0 -0
  64. bootstack/assets/elements/radio-unselected.png +0 -0
  65. bootstack/assets/elements/scrollbar-horizontal.png +0 -0
  66. bootstack/assets/elements/scrollbar-vertical.png +0 -0
  67. bootstack/assets/elements/slider-handle-focus.png +0 -0
  68. bootstack/assets/elements/slider-handle.png +0 -0
  69. bootstack/assets/elements/slider-track-horizontal.png +0 -0
  70. bootstack/assets/elements/slider-track-vertical.png +0 -0
  71. bootstack/assets/elements/switch-off.png +0 -0
  72. bootstack/assets/elements/switch-on.png +0 -0
  73. bootstack/assets/elements/tabs-bar-horizontal.png +0 -0
  74. bootstack/assets/elements/tabs-bar-vertical.png +0 -0
  75. bootstack/assets/elements/tabs-pill.png +0 -0
  76. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.mo +0 -0
  77. bootstack/assets/locales/ar/LC_MESSAGES/bootstack.po +853 -0
  78. bootstack/assets/locales/bg/LC_MESSAGES/bootstack.po +875 -0
  79. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.mo +0 -0
  80. bootstack/assets/locales/cs/LC_MESSAGES/bootstack.po +853 -0
  81. bootstack/assets/locales/da/LC_MESSAGES/bootstack.mo +0 -0
  82. bootstack/assets/locales/da/LC_MESSAGES/bootstack.po +853 -0
  83. bootstack/assets/locales/de/LC_MESSAGES/bootstack.mo +0 -0
  84. bootstack/assets/locales/de/LC_MESSAGES/bootstack.po +853 -0
  85. bootstack/assets/locales/en/LC_MESSAGES/bootstack.mo +0 -0
  86. bootstack/assets/locales/en/LC_MESSAGES/bootstack.po +875 -0
  87. bootstack/assets/locales/es/LC_MESSAGES/bootstack.mo +0 -0
  88. bootstack/assets/locales/es/LC_MESSAGES/bootstack.po +853 -0
  89. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.mo +0 -0
  90. bootstack/assets/locales/fr/LC_MESSAGES/bootstack.po +853 -0
  91. bootstack/assets/locales/he/LC_MESSAGES/bootstack.mo +0 -0
  92. bootstack/assets/locales/he/LC_MESSAGES/bootstack.po +851 -0
  93. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.mo +0 -0
  94. bootstack/assets/locales/hi/LC_MESSAGES/bootstack.po +842 -0
  95. bootstack/assets/locales/it/LC_MESSAGES/bootstack.mo +0 -0
  96. bootstack/assets/locales/it/LC_MESSAGES/bootstack.po +841 -0
  97. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.mo +0 -0
  98. bootstack/assets/locales/ja/LC_MESSAGES/bootstack.po +914 -0
  99. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.mo +0 -0
  100. bootstack/assets/locales/ko/LC_MESSAGES/bootstack.po +842 -0
  101. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.mo +0 -0
  102. bootstack/assets/locales/nb/LC_MESSAGES/bootstack.po +841 -0
  103. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.mo +0 -0
  104. bootstack/assets/locales/nl/LC_MESSAGES/bootstack.po +841 -0
  105. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.mo +0 -0
  106. bootstack/assets/locales/pl/LC_MESSAGES/bootstack.po +842 -0
  107. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.mo +0 -0
  108. bootstack/assets/locales/pt/LC_MESSAGES/bootstack.po +842 -0
  109. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.mo +0 -0
  110. bootstack/assets/locales/pt_BR/LC_MESSAGES/bootstack.po +842 -0
  111. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.mo +0 -0
  112. bootstack/assets/locales/sl/LC_MESSAGES/bootstack.po +842 -0
  113. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.mo +0 -0
  114. bootstack/assets/locales/sv/LC_MESSAGES/bootstack.po +842 -0
  115. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.mo +0 -0
  116. bootstack/assets/locales/tr/LC_MESSAGES/bootstack.po +842 -0
  117. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.mo +0 -0
  118. bootstack/assets/locales/zh_CN/LC_MESSAGES/bootstack.po +842 -0
  119. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.mo +0 -0
  120. bootstack/assets/locales/zh_TW/LC_MESSAGES/bootstack.po +842 -0
  121. bootstack/assets/themes/__init__.py +0 -0
  122. bootstack/assets/themes/amber-dark.json +32 -0
  123. bootstack/assets/themes/amber-light.json +32 -0
  124. bootstack/assets/themes/aurora-dark.json +32 -0
  125. bootstack/assets/themes/aurora-light.json +32 -0
  126. bootstack/assets/themes/bootstrap-dark.json +32 -0
  127. bootstack/assets/themes/bootstrap-light.json +32 -0
  128. bootstack/assets/themes/classic-dark.json +32 -0
  129. bootstack/assets/themes/classic-light.json +32 -0
  130. bootstack/assets/themes/docs-dark.json +32 -0
  131. bootstack/assets/themes/docs-light.json +32 -0
  132. bootstack/assets/themes/forest-dark.json +32 -0
  133. bootstack/assets/themes/forest-light.json +32 -0
  134. bootstack/assets/themes/ocean-dark.json +32 -0
  135. bootstack/assets/themes/ocean-light.json +32 -0
  136. bootstack/assets/themes/rose-dark.json +32 -0
  137. bootstack/assets/themes/rose-light.json +32 -0
  138. bootstack/assets/widgets/__init__.py +0 -0
  139. bootstack/assets/widgets/badge-default.png +0 -0
  140. bootstack/assets/widgets/badge-pill.png +0 -0
  141. bootstack/assets/widgets/border.png +0 -0
  142. bootstack/assets/widgets/button-group-horizontal-after.png +0 -0
  143. bootstack/assets/widgets/button-group-horizontal-before.png +0 -0
  144. bootstack/assets/widgets/button-group-horizontal-center.png +0 -0
  145. bootstack/assets/widgets/button-group-vertical-after.png +0 -0
  146. bootstack/assets/widgets/button-group-vertical-before.png +0 -0
  147. bootstack/assets/widgets/button-group-vertical-center.png +0 -0
  148. bootstack/assets/widgets/button.png +0 -0
  149. bootstack/assets/widgets/checkbox-checked.png +0 -0
  150. bootstack/assets/widgets/checkbox-indeterminate.png +0 -0
  151. bootstack/assets/widgets/checkbox-unchecked.png +0 -0
  152. bootstack/assets/widgets/field.png +0 -0
  153. bootstack/assets/widgets/icon-button.png +0 -0
  154. bootstack/assets/widgets/input-inner.png +0 -0
  155. bootstack/assets/widgets/input-prefix.png +0 -0
  156. bootstack/assets/widgets/input-suffix.png +0 -0
  157. bootstack/assets/widgets/input.png +0 -0
  158. bootstack/assets/widgets/list-item-focus.png +0 -0
  159. bootstack/assets/widgets/list-item-separated.png +0 -0
  160. bootstack/assets/widgets/menu-item-separated.png +0 -0
  161. bootstack/assets/widgets/notebook-client-border.png +0 -0
  162. bootstack/assets/widgets/notebook-pill-active.png +0 -0
  163. bootstack/assets/widgets/notebook-pill-inactive.png +0 -0
  164. bootstack/assets/widgets/notebook-tab-active.png +0 -0
  165. bootstack/assets/widgets/notebook-tab-border.png +0 -0
  166. bootstack/assets/widgets/notebook-tab-normal.png +0 -0
  167. bootstack/assets/widgets/notebook-underline.png +0 -0
  168. bootstack/assets/widgets/progress-bar-horizontal-default.png +0 -0
  169. bootstack/assets/widgets/progress-bar-horizontal-striped.png +0 -0
  170. bootstack/assets/widgets/progress-bar-vertical-default.png +0 -0
  171. bootstack/assets/widgets/progress-bar-vertical-striped.png +0 -0
  172. bootstack/assets/widgets/progress-trough-horizontal.png +0 -0
  173. bootstack/assets/widgets/progress-trough-vertical.png +0 -0
  174. bootstack/assets/widgets/radio-selected.png +0 -0
  175. bootstack/assets/widgets/radio-unselected.png +0 -0
  176. bootstack/assets/widgets/scrollbar-horizontal-rounded.png +0 -0
  177. bootstack/assets/widgets/scrollbar-vertical-rounded.png +0 -0
  178. bootstack/assets/widgets/separator-horizontal.png +0 -0
  179. bootstack/assets/widgets/separator-vertical.png +0 -0
  180. bootstack/assets/widgets/slider-handle-focus.png +0 -0
  181. bootstack/assets/widgets/slider-handle.png +0 -0
  182. bootstack/assets/widgets/slider-track-horizontal.png +0 -0
  183. bootstack/assets/widgets/slider-track-vertical.png +0 -0
  184. bootstack/assets/widgets/switch-off.png +0 -0
  185. bootstack/assets/widgets/switch-on.png +0 -0
  186. bootstack/assets/widgets/tabs-bar-horizontal.png +0 -0
  187. bootstack/assets/widgets/tabs-bar-vertical.png +0 -0
  188. bootstack/assets/widgets/tabs-pill.png +0 -0
  189. bootstack/cli/__init__.py +124 -0
  190. bootstack/cli/__main__.py +6 -0
  191. bootstack/cli/add.py +439 -0
  192. bootstack/cli/build.py +115 -0
  193. bootstack/cli/config.py +287 -0
  194. bootstack/cli/demo.py +1267 -0
  195. bootstack/cli/doctor.py +195 -0
  196. bootstack/cli/list_cmd.py +71 -0
  197. bootstack/cli/promote.py +120 -0
  198. bootstack/cli/pyinstaller.py +246 -0
  199. bootstack/cli/run.py +99 -0
  200. bootstack/cli/start.py +105 -0
  201. bootstack/cli/templates/__init__.py +861 -0
  202. bootstack/constants.py +325 -0
  203. bootstack/core/__init__.py +34 -0
  204. bootstack/core/capabilities/__init__.py +45 -0
  205. bootstack/core/capabilities/after.py +103 -0
  206. bootstack/core/capabilities/bind.py +154 -0
  207. bootstack/core/capabilities/bindtags.py +112 -0
  208. bootstack/core/capabilities/busy.py +61 -0
  209. bootstack/core/capabilities/clipboard.py +88 -0
  210. bootstack/core/capabilities/focus.py +118 -0
  211. bootstack/core/capabilities/grab.py +65 -0
  212. bootstack/core/capabilities/grid.py +188 -0
  213. bootstack/core/capabilities/localization.py +231 -0
  214. bootstack/core/capabilities/pack.py +119 -0
  215. bootstack/core/capabilities/place.py +92 -0
  216. bootstack/core/capabilities/selection.py +136 -0
  217. bootstack/core/capabilities/signals.py +242 -0
  218. bootstack/core/capabilities/winfo.py +315 -0
  219. bootstack/core/colorutils.py +234 -0
  220. bootstack/core/exceptions.py +95 -0
  221. bootstack/core/images.py +283 -0
  222. bootstack/core/localization/README.md +90 -0
  223. bootstack/core/localization/__init__.py +13 -0
  224. bootstack/core/localization/intl_format.py +580 -0
  225. bootstack/core/localization/msgcat.py +425 -0
  226. bootstack/core/localization/specs.py +143 -0
  227. bootstack/core/mixins/__init__.py +1 -0
  228. bootstack/core/mixins/ttk_state.py +35 -0
  229. bootstack/core/mixins/widget.py +132 -0
  230. bootstack/core/publisher.py +147 -0
  231. bootstack/core/signals/README.md +112 -0
  232. bootstack/core/signals/__init__.py +8 -0
  233. bootstack/core/signals/integration.py +100 -0
  234. bootstack/core/signals/signal.py +317 -0
  235. bootstack/core/signals/types.py +4 -0
  236. bootstack/core/validation/__init__.py +5 -0
  237. bootstack/core/validation/types.py +13 -0
  238. bootstack/core/validation/validation_result.py +17 -0
  239. bootstack/core/validation/validation_rules.py +112 -0
  240. bootstack/core/variables.py +62 -0
  241. bootstack/datasource/README.md +607 -0
  242. bootstack/datasource/__init__.py +51 -0
  243. bootstack/datasource/base.py +474 -0
  244. bootstack/datasource/file_source.py +541 -0
  245. bootstack/datasource/memory_source.py +482 -0
  246. bootstack/datasource/sqlite_source.py +453 -0
  247. bootstack/datasource/types.py +259 -0
  248. bootstack/dialogs/__init__.py +56 -0
  249. bootstack/dialogs/colorchooser.py +674 -0
  250. bootstack/dialogs/colordropper.py +257 -0
  251. bootstack/dialogs/datedialog.py +404 -0
  252. bootstack/dialogs/dialog.py +514 -0
  253. bootstack/dialogs/filterdialog.py +358 -0
  254. bootstack/dialogs/fontdialog.py +339 -0
  255. bootstack/dialogs/formdialog.py +541 -0
  256. bootstack/dialogs/message.py +489 -0
  257. bootstack/dialogs/query.py +561 -0
  258. bootstack/py.typed +1 -0
  259. bootstack/runtime/__init__.py +3 -0
  260. bootstack/runtime/app.py +879 -0
  261. bootstack/runtime/base_window.py +786 -0
  262. bootstack/runtime/events.py +399 -0
  263. bootstack/runtime/menu.py +510 -0
  264. bootstack/runtime/shortcuts.py +423 -0
  265. bootstack/runtime/tk_patch.py +31 -0
  266. bootstack/runtime/toplevel.py +131 -0
  267. bootstack/runtime/utility.py +371 -0
  268. bootstack/runtime/visual_focus.py +228 -0
  269. bootstack/runtime/window_utilities.py +1043 -0
  270. bootstack/style/__init__.py +5498 -0
  271. bootstack/style/bootstyle.py +507 -0
  272. bootstack/style/bootstyle_builder_base.py +752 -0
  273. bootstack/style/bootstyle_builder_mixed.py +93 -0
  274. bootstack/style/bootstyle_builder_tk.py +109 -0
  275. bootstack/style/bootstyle_builder_ttk.py +354 -0
  276. bootstack/style/builders/__init__.py +51 -0
  277. bootstack/style/builders/badge.py +44 -0
  278. bootstack/style/builders/button.py +453 -0
  279. bootstack/style/builders/buttongroup.py +344 -0
  280. bootstack/style/builders/calendar.py +271 -0
  281. bootstack/style/builders/checkbutton.py +95 -0
  282. bootstack/style/builders/combobox.py +112 -0
  283. bootstack/style/builders/contextmenu.py +268 -0
  284. bootstack/style/builders/entry.py +83 -0
  285. bootstack/style/builders/expander.py +171 -0
  286. bootstack/style/builders/field.py +312 -0
  287. bootstack/style/builders/frame.py +27 -0
  288. bootstack/style/builders/label.py +28 -0
  289. bootstack/style/builders/labelframe.py +41 -0
  290. bootstack/style/builders/listview.py +267 -0
  291. bootstack/style/builders/menubar.py +74 -0
  292. bootstack/style/builders/menubutton.py +408 -0
  293. bootstack/style/builders/notebook.py +316 -0
  294. bootstack/style/builders/panedwindow.py +25 -0
  295. bootstack/style/builders/progressbar.py +71 -0
  296. bootstack/style/builders/radiobutton.py +68 -0
  297. bootstack/style/builders/scale.py +66 -0
  298. bootstack/style/builders/scrollbar.py +360 -0
  299. bootstack/style/builders/separator.py +45 -0
  300. bootstack/style/builders/sidenav.py +313 -0
  301. bootstack/style/builders/sizegrip.py +15 -0
  302. bootstack/style/builders/spinbox.py +119 -0
  303. bootstack/style/builders/switch.py +67 -0
  304. bootstack/style/builders/tabitem.py +205 -0
  305. bootstack/style/builders/toolbutton.py +260 -0
  306. bootstack/style/builders/tooltip.py +26 -0
  307. bootstack/style/builders/treeview.py +269 -0
  308. bootstack/style/builders/utils.py +404 -0
  309. bootstack/style/builders_tk/__init__.py +16 -0
  310. bootstack/style/builders_tk/defaults.py +229 -0
  311. bootstack/style/element.py +173 -0
  312. bootstack/style/style.py +499 -0
  313. bootstack/style/theme_provider.py +449 -0
  314. bootstack/style/tk_patch.py +5 -0
  315. bootstack/style/token_maps.py +42 -0
  316. bootstack/style/types.py +32 -0
  317. bootstack/style/typography.py +527 -0
  318. bootstack/style/utility.py +696 -0
  319. bootstack/themes/__init__.py +12 -0
  320. bootstack/themes/standard.py +415 -0
  321. bootstack/themes/user.py +45 -0
  322. bootstack/widgets/__init__.py +53 -0
  323. bootstack/widgets/composites/__init__.py +38 -0
  324. bootstack/widgets/composites/accordion.py +385 -0
  325. bootstack/widgets/composites/appshell.py +445 -0
  326. bootstack/widgets/composites/buttongroup.py +391 -0
  327. bootstack/widgets/composites/calendar.py +914 -0
  328. bootstack/widgets/composites/compositeframe.py +282 -0
  329. bootstack/widgets/composites/contextmenu.py +1754 -0
  330. bootstack/widgets/composites/dateentry.py +261 -0
  331. bootstack/widgets/composites/dropdownbutton.py +190 -0
  332. bootstack/widgets/composites/expander.py +508 -0
  333. bootstack/widgets/composites/field.py +448 -0
  334. bootstack/widgets/composites/floodgauge.py +434 -0
  335. bootstack/widgets/composites/form.py +983 -0
  336. bootstack/widgets/composites/labeledscale.py +209 -0
  337. bootstack/widgets/composites/list/__init__.py +15 -0
  338. bootstack/widgets/composites/list/listitem.py +733 -0
  339. bootstack/widgets/composites/list/listview.py +1507 -0
  340. bootstack/widgets/composites/menubar.py +303 -0
  341. bootstack/widgets/composites/meter.py +882 -0
  342. bootstack/widgets/composites/numericentry.py +183 -0
  343. bootstack/widgets/composites/pagestack.py +330 -0
  344. bootstack/widgets/composites/passwordentry.py +149 -0
  345. bootstack/widgets/composites/pathentry.py +223 -0
  346. bootstack/widgets/composites/radiogroup.py +466 -0
  347. bootstack/widgets/composites/scrolledtext.py +388 -0
  348. bootstack/widgets/composites/scrolledtext.pyi +186 -0
  349. bootstack/widgets/composites/scrollview.py +675 -0
  350. bootstack/widgets/composites/selectbox.py +544 -0
  351. bootstack/widgets/composites/sidenav/__init__.py +24 -0
  352. bootstack/widgets/composites/sidenav/group.py +485 -0
  353. bootstack/widgets/composites/sidenav/header.py +83 -0
  354. bootstack/widgets/composites/sidenav/item.py +413 -0
  355. bootstack/widgets/composites/sidenav/separator.py +51 -0
  356. bootstack/widgets/composites/sidenav/view.py +919 -0
  357. bootstack/widgets/composites/spinnerentry.py +232 -0
  358. bootstack/widgets/composites/tableview/__init__.py +5 -0
  359. bootstack/widgets/composites/tableview/tableview.py +2254 -0
  360. bootstack/widgets/composites/tableview/types.py +169 -0
  361. bootstack/widgets/composites/tabs/__init__.py +6 -0
  362. bootstack/widgets/composites/tabs/tabitem.py +372 -0
  363. bootstack/widgets/composites/tabs/tabs.py +478 -0
  364. bootstack/widgets/composites/tabs/tabview.py +352 -0
  365. bootstack/widgets/composites/textentry.py +90 -0
  366. bootstack/widgets/composites/timeentry.py +189 -0
  367. bootstack/widgets/composites/toast.py +364 -0
  368. bootstack/widgets/composites/togglegroup.py +382 -0
  369. bootstack/widgets/composites/toolbar.py +393 -0
  370. bootstack/widgets/composites/tooltip.py +404 -0
  371. bootstack/widgets/internal/__init__.py +0 -0
  372. bootstack/widgets/internal/wrapper_base.py +304 -0
  373. bootstack/widgets/mixins/__init__.py +25 -0
  374. bootstack/widgets/mixins/configure_mixin.py +186 -0
  375. bootstack/widgets/mixins/entry_mixin.py +70 -0
  376. bootstack/widgets/mixins/font_mixin.py +346 -0
  377. bootstack/widgets/mixins/icon_mixin.py +38 -0
  378. bootstack/widgets/mixins/localization_mixin.py +255 -0
  379. bootstack/widgets/mixins/signal_mixin.py +272 -0
  380. bootstack/widgets/mixins/validation_mixin.py +204 -0
  381. bootstack/widgets/parts/__init__.py +11 -0
  382. bootstack/widgets/parts/numberentry_part.py +345 -0
  383. bootstack/widgets/parts/spinnerentry_part.py +394 -0
  384. bootstack/widgets/parts/textentry_part.py +344 -0
  385. bootstack/widgets/primitives/__init__.py +55 -0
  386. bootstack/widgets/primitives/badge.py +44 -0
  387. bootstack/widgets/primitives/button.py +89 -0
  388. bootstack/widgets/primitives/card.py +66 -0
  389. bootstack/widgets/primitives/checkbutton.py +124 -0
  390. bootstack/widgets/primitives/checktoggle.py +53 -0
  391. bootstack/widgets/primitives/combobox.py +165 -0
  392. bootstack/widgets/primitives/entry.py +98 -0
  393. bootstack/widgets/primitives/frame.py +206 -0
  394. bootstack/widgets/primitives/gridframe.py +479 -0
  395. bootstack/widgets/primitives/label.py +95 -0
  396. bootstack/widgets/primitives/labelframe.py +63 -0
  397. bootstack/widgets/primitives/menubutton.py +118 -0
  398. bootstack/widgets/primitives/notebook.py +551 -0
  399. bootstack/widgets/primitives/optionmenu.py +248 -0
  400. bootstack/widgets/primitives/packframe.py +228 -0
  401. bootstack/widgets/primitives/panedwindow.py +58 -0
  402. bootstack/widgets/primitives/progressbar.py +95 -0
  403. bootstack/widgets/primitives/radiobutton.py +115 -0
  404. bootstack/widgets/primitives/radiotoggle.py +50 -0
  405. bootstack/widgets/primitives/scale.py +85 -0
  406. bootstack/widgets/primitives/scrollbar.py +56 -0
  407. bootstack/widgets/primitives/separator.py +56 -0
  408. bootstack/widgets/primitives/sizegrip.py +47 -0
  409. bootstack/widgets/primitives/spinbox.py +91 -0
  410. bootstack/widgets/primitives/switch.py +41 -0
  411. bootstack/widgets/primitives/treeview.py +77 -0
  412. bootstack/widgets/types.py +20 -0
  413. bootstack-0.1.0a1.dist-info/METADATA +196 -0
  414. bootstack-0.1.0a1.dist-info/RECORD +419 -0
  415. bootstack-0.1.0a1.dist-info/WHEEL +5 -0
  416. bootstack-0.1.0a1.dist-info/entry_points.txt +2 -0
  417. bootstack-0.1.0a1.dist-info/licenses/LICENSE +22 -0
  418. bootstack-0.1.0a1.dist-info/licenses/NOTICE +10 -0
  419. bootstack-0.1.0a1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,394 @@
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, Union, List
10
+
11
+ from bootstack.core.localization import MessageCatalog, IntlFormatter
12
+ from bootstack.widgets.primitives.spinbox import Spinbox
13
+ from bootstack.widgets.mixins import ValidationMixin
14
+ from bootstack.widgets.mixins.configure_mixin import configure_delegate
15
+
16
+
17
+ class SpinnerEntryPart(ValidationMixin, Spinbox):
18
+ """Spinner widget with internationalization and value management.
19
+
20
+ This widget extends Spinbox to provide the same value/text management
21
+ pattern as TextEntryPart and NumberEntryPart. It can safely handle both
22
+ text and numeric values with optional formatting.
23
+
24
+ !!! note "Events"
25
+
26
+ - `<<Input>>`: Triggered on each keystroke.
27
+ Provides `event.data` with keys: `text`.
28
+
29
+ - `<<Change>>`: Triggered when value changes after commit.
30
+ Provides `event.data` with keys: `value`, `prev_value`, `text`.
31
+
32
+ - **<Return>**: Triggered on Enter key press.
33
+ Provides `event.data` with keys: `value`, `text`.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ master=None,
39
+ *,
40
+ value: Union[int, float, str] = '',
41
+ value_format: str = None,
42
+ values: List[str] = None,
43
+ from_: Union[int, float] = None,
44
+ to: Union[int, float] = None,
45
+ increment: Union[int, float] = 1,
46
+ wrap: bool = False,
47
+ initial_focus: bool = False,
48
+ allow_blank: bool = True,
49
+ **kwargs
50
+ ):
51
+ """Initialize a SpinnerEntryPart widget.
52
+
53
+ Creates a spinner widget that separates display text from the parsed value.
54
+ The widget defers parsing and formatting until the user commits the value
55
+ by pressing Enter or moving focus away from the widget.
56
+
57
+ Args:
58
+ master: Parent widget. If None, uses the default root window.
59
+ value: Initial value to display and parse. Can be a string, int, or float.
60
+ Default is empty string.
61
+ value_format: ICU format pattern for parsing and formatting the value.
62
+ Common patterns:
63
+ - Numbers: '#,##0.00' (decimal with thousands separator)
64
+ - Currency: '¤#,##0.00' (currency symbol with amount)
65
+ - Dates: 'yyyy-MM-dd' (ISO date format)
66
+ - Percent: '#,##0.00%' (percentage)
67
+ If None, value is treated as plain text (no parsing/formatting).
68
+ values: List of valid string values for the spinner. If provided,
69
+ the spinner cycles through these values. Mutually exclusive with
70
+ from_/to/increment.
71
+ from_: Minimum numeric value (inclusive). Only used for numeric spinners.
72
+ to: Maximum numeric value (inclusive). Only used for numeric spinners.
73
+ increment: Step size for increment/decrement. Default is 1.
74
+ wrap: If True, values wrap around at boundaries. Default is False.
75
+ initial_focus: If True, widget receives focus when created. Default is False.
76
+ allow_blank: If True, empty input is parsed as None. If False, empty
77
+ input preserves the previous value. Default is True.
78
+ **kwargs: Additional keyword arguments passed to SpinnerPart.
79
+
80
+ Note:
81
+ The widget automatically subscribes to text changes and sets up
82
+ event handlers for <FocusIn>, <FocusOut>, and <Return>.
83
+ """
84
+ # Store configuration
85
+ self._value_format = value_format
86
+ self._allow_blank = allow_blank
87
+ self._on_input_fid = None
88
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
89
+
90
+ # Configure Spinbox parameters
91
+ spinbox_kwargs = {}
92
+ if values is not None:
93
+ spinbox_kwargs['values'] = values
94
+ if from_ is not None:
95
+ spinbox_kwargs['from_'] = from_
96
+ if to is not None:
97
+ spinbox_kwargs['to'] = to
98
+ if increment is not None:
99
+ spinbox_kwargs['increment'] = increment
100
+ spinbox_kwargs['wrap'] = wrap
101
+
102
+ # Merge with user kwargs and set ttk_class/variant for field styling
103
+ spinbox_kwargs.update(kwargs)
104
+ spinbox_kwargs.setdefault('ttk_class', 'TField')
105
+ spinbox_kwargs.setdefault('variant', 'spinner')
106
+
107
+ # Initialize base Spinbox with ValidationMixin
108
+ super().__init__(master, **spinbox_kwargs)
109
+
110
+ # Set the initial display value
111
+ if isinstance(value, (int, float)):
112
+ initial_display = str(value)
113
+ else:
114
+ initial_display = value or self.textsignal.get() or ''
115
+
116
+ # Parse initial value if format is specified
117
+ if value_format is not None:
118
+ initial_value = self._parse_or_none(initial_display)
119
+ else:
120
+ initial_value = initial_display or ''
121
+
122
+ self._value = initial_value
123
+ self._prev_changed_value = initial_value
124
+
125
+ # Normalize initial display if we already have a parsed value
126
+ if self._value is not None:
127
+ formatted_text = self._format_value(self._value)
128
+ self.textsignal.set(formatted_text)
129
+ else:
130
+ self.textsignal.set('')
131
+
132
+ # Track last text emitted for input events
133
+ self._prev_change_text = self.textsignal.get()
134
+
135
+ # Subscribe to text changes
136
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
137
+
138
+ # Commit on focus out / enter
139
+ self.bind('<FocusIn>', self._store_prev_value, add=True)
140
+ self.bind('<FocusOut>', self._handle_focus_out, add=True)
141
+ self.bind('<Return>', self._handle_return, add=True)
142
+ self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
143
+
144
+ # Set initial focus
145
+ if initial_focus:
146
+ self.focus()
147
+
148
+ def _store_prev_value(self, _: Any):
149
+ """Store current value on focus-in to detect changes later."""
150
+ self._prev_changed_value = self._value
151
+
152
+ def _handle_focus_out(self, _):
153
+ """Commit value and check for changes when focus leaves the widget."""
154
+ self.commit()
155
+ self._check_if_changed()
156
+
157
+ def _handle_return(self, _):
158
+ """Commit value and check for changes when Return key is pressed."""
159
+ self.commit()
160
+ self._check_if_changed()
161
+
162
+ def _handle_change(self, event):
163
+ """Emit <<Input>> event on every text change without parsing."""
164
+ text = self.textsignal.get()
165
+ if text == self._prev_change_text:
166
+ return
167
+
168
+ self._prev_change_text = text
169
+ self.event_generate('<<Input>>', data={"text": text})
170
+
171
+ def _check_if_changed(self):
172
+ """Emit <<Change>> event if parsed value changed since focus-in."""
173
+ if self._value != self._prev_changed_value:
174
+ data = {
175
+ "value": self._value,
176
+ "prev_value": self._prev_changed_value,
177
+ "text": self.textsignal.get()
178
+ }
179
+ self.event_generate('<<Change>>', data={"value": self._value})
180
+ self._prev_changed_value = self._value
181
+
182
+ def _parse_or_none(self, s: str):
183
+ """Parse string using value_format, returning None on empty/invalid input."""
184
+ # If a non-string is passed (e.g., datetime/date), assume it's already parsed
185
+ if not isinstance(s, str):
186
+ return s
187
+ s2 = (s or '').strip()
188
+ if not s2:
189
+ return None
190
+ try:
191
+ if self._value_format is None:
192
+ return s2
193
+ return self._fmt.parse(s2, self._value_format)
194
+ except ValueError:
195
+ return None
196
+
197
+ def _format_value(self, value: Any) -> str:
198
+ """Format a value for display using value_format.
199
+
200
+ Args:
201
+ value: The value to format (can be string, int, float, etc.)
202
+
203
+ Returns:
204
+ Formatted string representation of the value
205
+ """
206
+ if value is None:
207
+ return ''
208
+
209
+ # If no format specified, just convert to string
210
+ if self._value_format is None:
211
+ return str(value)
212
+
213
+ # IntlFormatter.format() expects numeric values for numeric formats
214
+ try:
215
+ return self._fmt.format(value, self._value_format)
216
+ except (ValueError, TypeError):
217
+ # If formatting fails, return string representation
218
+ return str(value)
219
+
220
+ def on_input(self, callback: Callable[[Any], Any]) -> str:
221
+ """Bind callback to <<Input>> event (fires on every keystroke).
222
+
223
+ Args:
224
+ callback: Function receiving event.data = {"text": str}
225
+
226
+ Returns:
227
+ Binding identifier for use with off_input()
228
+ """
229
+ return self.bind('<<Input>>', callback, add=True)
230
+
231
+ def off_input(self, bind_id: str | None = None):
232
+ """Remove callback from <<Input>> event."""
233
+ self.unbind('<<Input>>', bind_id)
234
+
235
+ def on_enter(self, callback: Callable[[Any], Any]) -> str:
236
+ """Bind callback to <Return> event.
237
+
238
+ Args:
239
+ callback: Function receiving event.data = {"value": Any, "text": str}
240
+
241
+ Returns:
242
+ Binding identifier for use with off_enter()
243
+ """
244
+ def enrich_callback(event: Event) -> None:
245
+ data = {"value": self._value, "text": self.textsignal.get()}
246
+ event.data = data
247
+ return callback(event)
248
+
249
+ return self.bind('<Return>', enrich_callback, add=True)
250
+
251
+ def off_enter(self, bind_id: str | None = None):
252
+ """Remove callback from <Return> event."""
253
+ self.unbind('<Return>', bind_id)
254
+
255
+ def on_changed(self, callback: Callable[[Any], Any]) -> str:
256
+ """Bind callback to <<Change>> event (fires when value changes on commit).
257
+
258
+ Args:
259
+ callback: Function receiving event.data = {"value": Any, "prev_value": Any, "text": str}
260
+
261
+ Returns:
262
+ Binding identifier for use with off_changed()
263
+ """
264
+ return self.bind("<<Change>>", callback)
265
+
266
+ def off_changed(self, bind_id: str | None = None):
267
+ """Remove callback from <<Change>> event."""
268
+ self.unbind('<<Change>>', bind_id)
269
+
270
+ def value(self, value=None):
271
+ """Get or set the parsed/committed value.
272
+
273
+ Args:
274
+ value: If provided, sets the display text and internal value with formatting
275
+
276
+ Returns:
277
+ Current parsed value if no argument provided, None otherwise
278
+ """
279
+ if value is None:
280
+ return self._value
281
+ else:
282
+ # Store the value and format it for display
283
+ if isinstance(value, (int, float)):
284
+ value_str = str(value)
285
+ else:
286
+ value_str = str(value) if value is not None else ''
287
+
288
+ # Parse the value if format is specified
289
+ if self._value_format is not None and value_str:
290
+ self._value = self._parse_or_none(value_str)
291
+ else:
292
+ self._value = value_str if value_str else None
293
+
294
+ # Format and display
295
+ formatted_text = self._format_value(self._value)
296
+ self.textsignal.set(formatted_text)
297
+ return None
298
+
299
+ def text(self, value=None):
300
+ """Get or set the raw display text without committing.
301
+
302
+ Args:
303
+ value: If provided, sets the display text without parsing
304
+
305
+ Returns:
306
+ Current display text if no argument provided, None otherwise
307
+ """
308
+ if value is None:
309
+ return self.textsignal.get()
310
+ else:
311
+ self.textsignal.set(value)
312
+ return None
313
+
314
+ def commit(self):
315
+ """Parse display text, update value, and normalize display (called on FocusOut/Return)."""
316
+ s = self.get().strip()
317
+
318
+ # Parse once
319
+ if s == '':
320
+ self._value = None if self._allow_blank else self._value
321
+ else:
322
+ try:
323
+ self._value = s if self._value_format is None else self._fmt.parse(s, self._value_format)
324
+ except ValueError:
325
+ # Keep prior value on parse failure
326
+ return
327
+
328
+ # Format the value for display
329
+ new_text = self._format_value(self._value)
330
+
331
+ if new_text != self.textsignal.get():
332
+ # Temporarily silence input events while normalizing text
333
+ fid = getattr(self, '_on_input_fid', None)
334
+ if fid:
335
+ try:
336
+ self.textsignal.unsubscribe(fid)
337
+ except TclError:
338
+ pass
339
+ self.textsignal.set(new_text)
340
+ if fid:
341
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
342
+
343
+ def _on_locale_changed(self, event=None):
344
+ """Respond to global changes in locale by updating formatter and text."""
345
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
346
+
347
+ # Reformat current value with new locale + same value_format
348
+ if self._value is not None:
349
+ formatted_text = self._format_value(self._value)
350
+ self.textsignal.set(formatted_text)
351
+
352
+ @configure_delegate('text')
353
+ def _delegate_text(self, value=None):
354
+ if value is None:
355
+ return self.text()
356
+ else:
357
+ return self.text(value)
358
+
359
+ @configure_delegate('value')
360
+ def _delegate_value(self, value=None):
361
+ if value is None:
362
+ return self.value()
363
+ else:
364
+ return self.value(value)
365
+
366
+ @configure_delegate('value_format')
367
+ def _delegate_value_format(self, value: str):
368
+ """Get or set the value format pattern and reformat display."""
369
+ if value is None:
370
+ return self._value_format
371
+
372
+ self._value_format = value
373
+ # Reformat current value with new format
374
+ if self._value is not None:
375
+ formatted_text = self._format_value(self._value)
376
+ self.textsignal.set(formatted_text)
377
+
378
+ @configure_delegate('allow_blank')
379
+ def _delegate_allow_blank(self, value: bool):
380
+ """Get or set whether blank input is allowed."""
381
+ if value is None:
382
+ return self._allow_blank
383
+
384
+ self._allow_blank = bool(value)
385
+
386
+ def destroy(self):
387
+ """Clean up signal subscriptions and destroy the widget."""
388
+ if self._on_input_fid:
389
+ try:
390
+ self.textsignal.unsubscribe(self._on_input_fid)
391
+ except Exception:
392
+ pass # Ignore all errors during cleanup
393
+ self._on_input_fid = None
394
+ super().destroy()