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,317 @@
1
+ import tkinter as tk
2
+ import weakref
3
+ from itertools import count
4
+ from typing import Any, Callable, Generic, Type, TypeVar
5
+
6
+ from bootstack.core.signals.types import TraceOperation
7
+ from bootstack.core.variables import SetVar
8
+
9
+ T = TypeVar("T")
10
+ U = TypeVar("U")
11
+
12
+
13
+ class _SignalTrace:
14
+ """
15
+ Internal helper to manage Tcl variable traces using tkinter's Variable API.
16
+ This class encapsulates low-level `trace_add` and `trace_remove` logic.
17
+ """
18
+
19
+ def __init__(self, tk_var: tk.Variable):
20
+ """
21
+ Initialize a trace manager for a tkinter variable.
22
+
23
+ Args:
24
+ tk_var: A tkinter.Variable instance (e.g., StringVar, IntVar).
25
+ """
26
+ self._var = tk_var
27
+ # Map trace id -> (operation, callback)
28
+ self._traces: dict[str, tuple[TraceOperation, Callable[..., Any]]] = {}
29
+
30
+ def callbacks(self) -> tuple[str, ...]:
31
+ """
32
+ Return all currently active trace IDs.
33
+
34
+ Returns:
35
+ A tuple of trace ID strings.
36
+ """
37
+ return tuple(self._traces.keys())
38
+
39
+ def add(
40
+ self,
41
+ operation: TraceOperation,
42
+ callback: Callable[[T], Any],
43
+ get_value: Callable[[], T],
44
+ ) -> str:
45
+ """
46
+ Add a new trace that calls a callback when the variable is written.
47
+ """
48
+
49
+ def traced_callback(name: str, index: str, mode: str) -> None:
50
+ callback(get_value())
51
+
52
+ try:
53
+ fid = self._var.trace_add(operation, traced_callback)
54
+ except tk.TclError as e:
55
+ raise RuntimeError(f"failed to add trace: {e}") from e
56
+ self._traces[fid] = (operation, traced_callback)
57
+ return fid
58
+
59
+ def remove(self, fid: str) -> None:
60
+ """
61
+ Remove a trace by ID. Safe if already removed or variable destroyed.
62
+ """
63
+ op_cb = self._traces.pop(fid, None)
64
+ if op_cb is None:
65
+ return
66
+ operation, _ = op_cb
67
+ try:
68
+ self._var.trace_remove(operation, fid)
69
+ except tk.TclError:
70
+ # Variable may be unset/destroyed; ignore
71
+ pass
72
+
73
+
74
+ class Signal(Generic[T]):
75
+ """
76
+ A reactive signal backed by a tkinter Variable.
77
+
78
+ Supports value access, transformation via `.map()`, and subscription
79
+ to change events via `.subscribe()`.
80
+
81
+ Can be passed to Tkinter widgets using `str(signal)` or `signal.name`.
82
+ """
83
+
84
+ _cnt = count(1)
85
+
86
+ def __init__(self, value: T, name: str | None = None, master: tk.Misc | None = None):
87
+ self._name = name or f"SIG{next(self._cnt)}"
88
+ self._type: Type[T] = type(value)
89
+ self._master: tk.Misc | None = master
90
+ self._var = self._create_variable(value)
91
+ self._trace = _SignalTrace(self._var)
92
+ # Map fid -> callback to allow multiple subscriptions of same function
93
+ self._subscribers: dict[str, Callable[[T], Any]] = {}
94
+ # Reverse index: callback -> set of fids
95
+ self._callback_index: dict[Callable[[T], Any], set[str]] = {}
96
+ # Cache last known value for robustness when Tcl variable is torn down
97
+ self._last: T = value
98
+
99
+ def _create_variable(self, value: T) -> tk.Variable:
100
+ if isinstance(value, bool):
101
+ return tk.BooleanVar(master=self._master, name=self._name, value=value)
102
+ elif isinstance(value, int):
103
+ return tk.IntVar(master=self._master, name=self._name, value=value)
104
+ elif isinstance(value, float):
105
+ return tk.DoubleVar(master=self._master, name=self._name, value=value)
106
+ elif isinstance(value, set):
107
+ return SetVar(master=self._master, name=self._name, value=value)
108
+ else:
109
+ return tk.StringVar(master=self._master, name=self._name, value=value)
110
+
111
+ def __call__(self) -> T:
112
+ """
113
+ Get the current value of the signal.
114
+
115
+ Returns:
116
+ The current typed value.
117
+ """
118
+ try:
119
+ value = self._var.get()
120
+ self._last = value # cache last good value
121
+ return value
122
+ except tk.TclError:
123
+ # Return last known value when underlying var is destroyed/unset
124
+ return self._last
125
+
126
+ def get(self) -> T:
127
+ """Return the current value of the signal.
128
+
129
+ Alias for calling the instance directly (`signal()`). Mirrors
130
+ tkinter's `Variable.get` naming for consistency.
131
+ """
132
+ return self()
133
+
134
+ @classmethod
135
+ def from_variable(
136
+ cls,
137
+ tk_var: tk.Variable,
138
+ *,
139
+ name: str | None = None,
140
+ coerce: Type[T] | None = None,
141
+ ) -> "Signal[T]":
142
+ """
143
+ Wrap an existing tkinter Variable as a Signal.
144
+
145
+ Args:
146
+ tk_var: An existing tkinter.Variable instance (StringVar, IntVar, etc.).
147
+ name: Optional override of the Tcl variable name. Defaults to tk_var's name.
148
+ coerce: Optional Python type to treat the signal as (e.g., int/float/bool/str).
149
+ If omitted, the type is inferred from the tk_var subclass.
150
+
151
+ Returns:
152
+ A Signal bound to the provided tk_var.
153
+ """
154
+ # Infer Python type from the tk variable if not explicitly provided
155
+ if coerce is None:
156
+ if isinstance(tk_var, tk.BooleanVar):
157
+ py_type: Type[Any] = bool
158
+ elif isinstance(tk_var, tk.IntVar):
159
+ py_type = int
160
+ elif isinstance(tk_var, tk.DoubleVar):
161
+ py_type = float
162
+ elif isinstance(tk_var, SetVar):
163
+ py_type = set
164
+ else:
165
+ py_type = str
166
+ else:
167
+ py_type = coerce
168
+
169
+ # Construct without creating a new tk.Variable
170
+ self = cls.__new__(cls) # bypass __init__
171
+ self._name = name or getattr(tk_var, "_name", str(tk_var))
172
+ self._type = py_type # type: ignore[assignment]
173
+ self._var = tk_var
174
+ self._trace = _SignalTrace(self._var)
175
+ self._subscribers = {}
176
+ self._callback_index = {}
177
+ # Best-effort capture of master/interpreter for reference
178
+ self._master = getattr(tk_var, "_master", None)
179
+ try:
180
+ current = tk_var.get() # type: ignore[assignment]
181
+ except tk.TclError:
182
+ # Fallback default per inferred type
183
+ if py_type is float: # type: ignore[comparison-overlap]
184
+ current = 0.0
185
+ elif py_type is int: # type: ignore[comparison-overlap]
186
+ current = 0
187
+ elif py_type is bool: # type: ignore[comparison-overlap]
188
+ current = False
189
+ elif py_type is set:
190
+ current = set()
191
+ else:
192
+ current = ""
193
+ self._last = current # type: ignore[assignment]
194
+ return self
195
+
196
+ def set(self, value: T) -> None:
197
+ """
198
+ Set the signal to a new value and notify subscribers.
199
+
200
+ Args:
201
+ value: The new value. Must match the original type.
202
+
203
+ Raises:
204
+ TypeError: If the value type does not match the original.
205
+ """
206
+ # Enforce exact type to avoid bool being accepted for int, etc.
207
+ if type(value) is not self._type:
208
+ raise TypeError(f"Expected {self._type.__name__}, got {type(value).__name__}")
209
+ # Reduce redundant updates if value unchanged
210
+ try:
211
+ current = self._var.get()
212
+ if current == value:
213
+ return
214
+ except tk.TclError:
215
+ # If var is gone, proceed to set and let Tcl recreate path if possible
216
+ pass
217
+ self._var.set(value)
218
+ self._last = value
219
+
220
+ def map(self, transform: Callable[[T], U]) -> 'Signal[U]':
221
+ """
222
+ Create a derived signal that transforms this signal's value.
223
+
224
+ Args:
225
+ transform: A function applied to the current and future values.
226
+
227
+ Returns:
228
+ A new Signal[U] that stays updated with the transformed value.
229
+ """
230
+ derived = Signal(transform(self()))
231
+
232
+ # Use weakref to avoid keeping derived alive solely via subscription
233
+ weak_derived = weakref.ref(derived)
234
+
235
+ def update(value: T) -> None:
236
+ d = weak_derived()
237
+ if d is None:
238
+ # Auto-detach if derived is GC'd
239
+ return
240
+ d.set(transform(value))
241
+
242
+ self.subscribe(update)
243
+ return derived
244
+
245
+ def subscribe(self, callback: Callable[[T], Any], *, immediate: bool = False) -> str:
246
+ """
247
+ Subscribe to value changes of this signal.
248
+
249
+ Args:
250
+ callback: A function that receives the current value (T) when updated.
251
+
252
+ Returns:
253
+ A trace ID that can be used for removal.
254
+ """
255
+ fid = self._trace.add("write", callback, self)
256
+ self._subscribers[fid] = callback
257
+ self._callback_index.setdefault(callback, set()).add(fid)
258
+ if immediate:
259
+ try:
260
+ callback(self())
261
+ except Exception:
262
+ # Do not fail subscription due to callback error
263
+ pass
264
+ return fid
265
+
266
+ def unsubscribe(self, funcid: str) -> None:
267
+ """
268
+ Remove a previously registered subscriber.
269
+
270
+ Args:
271
+ funcid: The function id returned from `subscribe()`.
272
+ """
273
+ self._subscribers.pop(funcid, None)
274
+ self._trace.remove(funcid)
275
+
276
+ def unsubscribe_all(self) -> None:
277
+ """
278
+ Remove all currently subscribed callbacks.
279
+ """
280
+ # Copy keys to avoid mutation during iteration
281
+ for fid in list(self._subscribers.keys()):
282
+ self._trace.remove(fid)
283
+ self._subscribers.clear()
284
+ self._callback_index.clear()
285
+
286
+ def __getattr__(self, name: str) -> Any:
287
+ """
288
+ Proxy access to the underlying tk.Variable instance.
289
+ """
290
+ return getattr(self._var, name)
291
+
292
+ @property
293
+ def name(self) -> str:
294
+ """
295
+ Return the Tcl name of the variable (for use in widget `textvariable`).
296
+ """
297
+ return self._name
298
+
299
+ @property
300
+ def type(self) -> Type[T]:
301
+ """
302
+ The original type of the signal value.
303
+
304
+ Returns:
305
+ A Python type (e.g., int, str).
306
+ """
307
+ return self._type
308
+
309
+ @property
310
+ def var(self) -> tk.Variable:
311
+ return self._var
312
+
313
+ def __str__(self) -> str:
314
+ return self._name
315
+
316
+ def __repr__(self) -> str:
317
+ return self._name
@@ -0,0 +1,4 @@
1
+ """Type aliases for the signal and variable tracing system."""
2
+ from typing import Literal
3
+
4
+ TraceOperation = Literal["array", "read", "write", "unset"]
@@ -0,0 +1,5 @@
1
+ """Validation rules and results for Field-based input widgets."""
2
+ from .validation_rules import ValidationRule
3
+ from .validation_result import ValidationResult
4
+
5
+ __all__ = ['ValidationRule', 'ValidationResult']
@@ -0,0 +1,13 @@
1
+ from typing import Callable, Literal, Optional, TypedDict
2
+
3
+ RuleType = Literal["required", "email", "pattern", "stringLength", "custom", "compare"]
4
+ RuleTriggerType = Literal['key', 'blur', 'always', 'manual']
5
+
6
+
7
+ class ValidationOptions(TypedDict, total=False):
8
+ pattern: str
9
+ message: str
10
+ min: int
11
+ max: int
12
+ trigger: Optional[Literal["key", "blur", "always", "manual"]]
13
+ func: Callable[[str], bool]
@@ -0,0 +1,17 @@
1
+ class ValidationResult:
2
+ """The outcome of a single validation check.
3
+
4
+ Attributes:
5
+ is_valid (bool): True if validation passed, False otherwise.
6
+ message (str): Error message when `is_valid` is False; empty string otherwise.
7
+ """
8
+
9
+ def __init__(self, is_valid: bool, message: str = ""):
10
+ """Create a validation result.
11
+
12
+ Args:
13
+ is_valid: True if validation passed.
14
+ message: Error message. Defaults to an empty string.
15
+ """
16
+ self.is_valid = is_valid
17
+ self.message = message
@@ -0,0 +1,112 @@
1
+ import re
2
+ from typing import Callable
3
+
4
+ from bootstack.core.validation.types import RuleTriggerType, RuleType
5
+ from bootstack.core.validation.validation_result import ValidationResult
6
+
7
+
8
+ class ValidationRule:
9
+ """A single validation rule that can be applied to a string value.
10
+
11
+ Supports the built-in rule types `'required'`, `'email'`,
12
+ `'stringLength'`, `'pattern'`, and `'custom'`, and carries a trigger
13
+ policy that controls when the rule is evaluated.
14
+
15
+ Attributes:
16
+ type (RuleType): The validation rule type.
17
+ message (str): Custom error message; if empty a default is generated.
18
+ trigger (RuleTriggerType): When the rule fires — `'always'`, `'blur'`, or `'manual'`.
19
+ params (dict): Additional parameters specific to the rule type
20
+ (e.g., `min`/`max` for `'stringLength'`, `pattern` for `'pattern'`,
21
+ `func` for `'custom'`).
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ rule_type: RuleType,
27
+ message: str = "",
28
+ **kwargs
29
+ ):
30
+ """Create a validation rule.
31
+
32
+ Args:
33
+ rule_type: The type of validation to apply.
34
+ message: Custom error message. If empty, a sensible default is used.
35
+ **kwargs: Rule-specific parameters. Pass `trigger` to override the
36
+ default trigger policy; all other keys are stored in `params`
37
+ (e.g., `min=3, max=20` for `'stringLength'`, `pattern=r'\\d+'`
38
+ for `'pattern'`, `func=callable` for `'custom'`).
39
+ """
40
+ self.type = rule_type
41
+ self.message = message
42
+ self.trigger = kwargs.pop('trigger', self._default_trigger())
43
+ self.params = kwargs
44
+
45
+ def validate(self, value: str) -> ValidationResult:
46
+ """Apply this rule to a value and return the result.
47
+
48
+ Args:
49
+ value: The string value to validate.
50
+
51
+ Returns:
52
+ A ValidationResult with `is_valid=True` on success or `is_valid=False`
53
+ with an error message on failure.
54
+ """
55
+ msg = self.message or self._default_message()
56
+
57
+ if self.type == "required":
58
+ if value is None:
59
+ return ValidationResult(False, msg)
60
+ if isinstance(value, str) and not value.strip():
61
+ return ValidationResult(False, msg)
62
+ # Everything else is valid (non-empty string, number, date, etc.)
63
+ return ValidationResult(True, "")
64
+
65
+ elif self.type == "email":
66
+ if not re.match(r"[^@]+@[^@]+\.[^@]+", value):
67
+ return ValidationResult(False, msg)
68
+ elif self.type == "stringLength":
69
+ min_len = self.params.get("min", 0)
70
+ max_len = self.params.get("max", float("inf"))
71
+ if not (min_len <= len(value) <= max_len):
72
+ return ValidationResult(False, msg)
73
+ elif self.type == "pattern":
74
+ pattern = self.params.get("pattern", "")
75
+ if not re.match(pattern, value):
76
+ return ValidationResult(False, msg)
77
+ elif self.type == "custom":
78
+ func: Callable[[str], bool] = self.params.get("func")
79
+ if func and not func(value):
80
+ return ValidationResult(False, msg)
81
+
82
+ return ValidationResult(True)
83
+
84
+ def _default_message(self) -> str:
85
+ """Return a sensible default error message for this rule type."""
86
+ if self.type == "required":
87
+ return "This field is required."
88
+ elif self.type == "email":
89
+ return "Enter a valid email address."
90
+ elif self.type == "stringLength":
91
+ min_len = self.params.get("min", 0)
92
+ max_len = self.params.get("max", None)
93
+ if max_len is None or max_len == float("inf"):
94
+ return f"Enter at least {min_len} characters."
95
+ return f"Enter between {min_len} and {max_len} characters."
96
+ elif self.type == "pattern":
97
+ return "Value does not match the required pattern."
98
+ elif self.type == "custom":
99
+ return "Invalid value."
100
+ return "Invalid input."
101
+
102
+ def _default_trigger(self) -> RuleTriggerType:
103
+ """Return the default trigger policy for this rule type."""
104
+ if self.type == "required":
105
+ return "always"
106
+ elif self.type in {"stringLength"}:
107
+ return "blur"
108
+ elif self.type in {"email", "pattern"}:
109
+ return "always"
110
+ elif self.type in {"custom"}:
111
+ return "manual"
112
+ return "blur"
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ import tkinter
5
+
6
+
7
+ class SetVar(tkinter.Variable):
8
+ """
9
+ A tkinter variable that holds a set of values.
10
+
11
+ This variable serializes a Python set into its string representation
12
+ for storage and deserializes it back into a set on retrieval.
13
+ """
14
+
15
+ def __init__(self, master=None, value: set | None = None, name: str | None = None):
16
+ """
17
+ Initialize the SetVar.
18
+
19
+ Args:
20
+ master (Widget, optional): The parent widget. Defaults to None.
21
+ value (set, optional): The initial value. Defaults to an empty set.
22
+ name (str, optional): The name of the variable. Defaults to None.
23
+ """
24
+ if value is None:
25
+ value = set()
26
+ super().__init__(master, value, name)
27
+
28
+ def set(self, value: set | frozenset):
29
+ """
30
+ Set the variable to a new value.
31
+
32
+ The value is converted to its string representation for storage.
33
+
34
+ Args:
35
+ value (set | frozenset): The new value. Should be a set or frozenset.
36
+ """
37
+ if not isinstance(value, (set, frozenset)):
38
+ raise TypeError(f"Expected set or frozenset, got {type(value).__name__}")
39
+ super().set(repr(value))
40
+
41
+ def get(self) -> set:
42
+ """
43
+ Return the value of the variable as a Python set.
44
+
45
+ The string representation is deserialized back into a set.
46
+
47
+ Returns:
48
+ set: The current value of the variable.
49
+ """
50
+ value_str = super().get()
51
+ if not value_str:
52
+ return set()
53
+
54
+ try:
55
+ # Use literal_eval for safe evaluation of the string representation
56
+ deserialized_value = ast.literal_eval(value_str)
57
+ if isinstance(deserialized_value, (set, frozenset)):
58
+ return set(deserialized_value)
59
+ else:
60
+ return set()
61
+ except (ValueError, SyntaxError):
62
+ return set()