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,344 @@
1
+ from tkinter import Event, TclError
2
+ from typing import Any, Callable
3
+
4
+ from bootstack.core.localization import MessageCatalog, IntlFormatter
5
+ from bootstack.widgets.primitives.entry import Entry
6
+ from bootstack.widgets.mixins import ValidationMixin
7
+ from bootstack.widgets.mixins.configure_mixin import configure_delegate
8
+
9
+
10
+ class TextEntryPart(ValidationMixin, Entry):
11
+ """Internationalization-aware entry widget with deferred parsing and formatting.
12
+
13
+ This widget separates user input (display text) from the committed/parsed value,
14
+ providing a clean pattern for handling formatted data entry. Parsing and formatting
15
+ only occur when the user commits the value via `<FocusOut>` or `<Return>`.
16
+
17
+ !!! note "Events"
18
+ - `<<Input>>`: Triggered on each keystroke. `event.data = {'text': str}`
19
+ - `<<Change>>`: Triggered when value changes after commit. `event.data = {'value': Any, 'prev_value': Any, 'text': str}`
20
+ - `<Return>`: Triggered on Enter key press. `event.data = {'value': Any, 'text': str}`
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ master=None,
26
+ *,
27
+ value='',
28
+ value_format=None,
29
+ initial_focus: bool = False,
30
+ allow_blank: bool = True,
31
+ **kwargs
32
+ ):
33
+ """Initialize a TextEntryPart widget with internationalization support.
34
+
35
+ Creates an entry widget that separates display text from the parsed value.
36
+ The widget defers parsing and formatting until the user commits the value
37
+ by pressing Enter or moving focus away from the widget.
38
+
39
+ Args:
40
+ master: Parent widget. If None, uses the default root window.
41
+ value: Initial value to display and parse. Can be a string or any value
42
+ that can be formatted using value_format. Default is empty string.
43
+ value_format (str): ICU format pattern for parsing and formatting the value.
44
+ Common patterns: `'#,##0.00'` (decimal), `'¤#,##0.00'` (currency),
45
+ `'yyyy-MM-dd'` (date), `'#,##0.00%'` (percent).
46
+ If None, value is treated as plain text (no parsing/formatting).
47
+ initial_focus (bool): If True, widget receives focus when created.
48
+ allow_blank (bool): If True, empty input is parsed as None. If False, empty
49
+ input preserves the previous value.
50
+ **kwargs: Additional keyword arguments passed to the Entry base class.
51
+
52
+ Note:
53
+ The widget automatically subscribes to text changes and sets up
54
+ event handlers for `<FocusIn>`, `<FocusOut>`, and `<Return>`.
55
+ """
56
+ kwargs.setdefault('ttk_class', 'TField')
57
+ kwargs.setdefault('variant', 'input')
58
+ super().__init__(master, **kwargs)
59
+
60
+ # configuration
61
+ self._value_format = value_format
62
+ self._allow_blank = allow_blank
63
+ self._on_input_fid = None
64
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
65
+
66
+ # set the initial display value
67
+ # Convert to string if it's a number
68
+ if isinstance(value, (int, float)):
69
+ initial_display = str(value)
70
+ else:
71
+ initial_display = value or self.textsignal.get() or ''
72
+
73
+ # Parse initial value if format is specified
74
+ if value_format is not None:
75
+ initial_value = self._parse_or_none(initial_display)
76
+ else:
77
+ initial_value = initial_display or ''
78
+
79
+ self._value = initial_value
80
+ self._prev_changed_value = initial_value
81
+
82
+ # normalize initial display if we already have a parsed value
83
+ if self._value is not None:
84
+ formatted_text = self._format_value(self._value)
85
+ self.textsignal.set(formatted_text)
86
+ else:
87
+ self.textsignal.set('')
88
+
89
+ # track last text emitted for CHANGE
90
+ self._prev_change_text = self.textsignal.get()
91
+
92
+ # subscribe to text changes
93
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
94
+
95
+ # Commit on focus out / enter;
96
+ self.bind('<FocusIn>', self._store_prev_value, add=True)
97
+ self.bind('<FocusOut>', self._handle_focus_out, add=True)
98
+ self.bind('<Return>', self._handle_return, add=True)
99
+ self.winfo_toplevel().bind('<<LocaleChanged>>', self._on_locale_changed, add='+')
100
+
101
+ # set initial focus
102
+ if initial_focus:
103
+ self.focus()
104
+
105
+ def _store_prev_value(self, _: Any):
106
+ """Store current value on focus-in to detect changes later."""
107
+ self._prev_changed_value = self._value
108
+
109
+ def _handle_focus_out(self, _):
110
+ """Commit value and check for changes when focus leaves the widget."""
111
+ self.commit()
112
+ self._check_if_changed()
113
+
114
+ def _handle_return(self, _):
115
+ """Commit value and check for changes when Return key is pressed."""
116
+ self.commit()
117
+ self._check_if_changed()
118
+
119
+ def _handle_change(self, event):
120
+ """Emit <<Input>> event on every text change without parsing."""
121
+ text = self.textsignal.get()
122
+ if text == self._prev_change_text:
123
+ return
124
+
125
+ self._prev_change_text = text
126
+ self.event_generate('<<Input>>', data={"text": text})
127
+
128
+ def _check_if_changed(self):
129
+ """Emit <<Change>> event if parsed value changed since focus-in."""
130
+ if self._value != self._prev_changed_value:
131
+ data = {
132
+ "value": self._value,
133
+ "prev_value": self._prev_changed_value,
134
+ "text": self.textsignal.get()
135
+ }
136
+ self.event_generate('<<Change>>', data=data)
137
+ self._prev_changed_value = self._value
138
+
139
+ def _parse_or_none(self, s: str):
140
+ """Parse string using value_format, returning None on empty/invalid input."""
141
+ # If a non-string is passed (e.g., datetime/date), assume it's already parsed.
142
+ if not isinstance(s, str):
143
+ return s
144
+ s2 = (s or '').strip()
145
+ if not s2:
146
+ return None
147
+ try:
148
+ if self._value_format is None:
149
+ return s2
150
+ return self._fmt.parse(s2, self._value_format)
151
+ except ValueError:
152
+ return None
153
+
154
+ def _format_value(self, value: Any) -> str:
155
+ """Format a value for display using value_format.
156
+
157
+ Args:
158
+ value: The value to format (can be string, int, float, etc.)
159
+
160
+ Returns:
161
+ Formatted string representation of the value
162
+ """
163
+ if value is None:
164
+ return ''
165
+
166
+ # If no format specified, just convert to string
167
+ if self._value_format is None:
168
+ return str(value)
169
+
170
+ # IntlFormatter.format() expects numeric values, not strings
171
+ # So pass the value directly (it will be a number from parse())
172
+ try:
173
+ return self._fmt.format(value, self._value_format)
174
+ except (ValueError, TypeError):
175
+ # If formatting fails, return string representation
176
+ return str(value)
177
+
178
+ def on_input(self, callback: Callable) -> str:
179
+ """Bind to `<<Input>>`. Callback receives `event.data = {'text': str}`."""
180
+ return self.bind('<<Input>>', callback, add=True)
181
+
182
+ def off_input(self, bind_id: str | None = None) -> None:
183
+ """Unbind from `<<Input>>`."""
184
+ self.unbind('<<Input>>', bind_id)
185
+
186
+ def on_enter(self, callback: Callable) -> str:
187
+ """Bind to `<Return>`. Callback receives `event.data = {'value': Any, 'text': str}`."""
188
+
189
+ def enrich_callback(event: Event) -> None:
190
+ data = {"value": self._value, "text": self.textsignal.get()}
191
+ event.data = data
192
+ return callback(event)
193
+
194
+ return self.bind('<Return>', enrich_callback, add=True)
195
+
196
+ def off_enter(self, bind_id: str | None = None) -> None:
197
+ """Unbind from `<Return>`."""
198
+ self.unbind('<Return>', bind_id)
199
+
200
+ def on_changed(self, callback: Callable) -> str:
201
+ """Bind to `<<Change>>`. Callback receives `event.data = {'value': Any, 'prev_value': Any, 'text': str}`."""
202
+ return self.bind("<<Change>>", callback, add=True)
203
+
204
+ def off_changed(self, bind_id: str | None = None) -> None:
205
+ """Unbind from `<<Change>>`."""
206
+ self.unbind('<<Change>>', bind_id)
207
+
208
+ def value(self, value=None):
209
+ """Get or set the parsed/committed value.
210
+
211
+ Args:
212
+ value: If provided, sets the display text and internal value with formatting
213
+
214
+ Returns:
215
+ Current parsed value if no argument provided, None otherwise
216
+ """
217
+ if value is None:
218
+ return self._value
219
+ else:
220
+ # Store the value and format it for display
221
+ if isinstance(value, (int, float)):
222
+ value_str = str(value)
223
+ else:
224
+ value_str = str(value) if value is not None else ''
225
+
226
+ # Parse the value if format is specified
227
+ if self._value_format is not None and value_str:
228
+ self._value = self._parse_or_none(value_str)
229
+ else:
230
+ self._value = value_str if value_str else None
231
+
232
+ # Format and display
233
+ formatted_text = self._format_value(self._value)
234
+ self.textsignal.set(formatted_text)
235
+ return None
236
+
237
+ def text(self, value=None):
238
+ """Get or set the raw display text without committing.
239
+
240
+ Args:
241
+ value: If provided, sets the display text without parsing
242
+
243
+ Returns:
244
+ Current display text if no argument provided, None otherwise
245
+ """
246
+ if value is None:
247
+ return self.textsignal.get()
248
+ else:
249
+ self.textsignal.set(value)
250
+ return None
251
+
252
+ def commit(self):
253
+ """Parse display text, update value, and normalize display (called on FocusOut/Return)."""
254
+ s = self.get().strip()
255
+
256
+ # parse once
257
+ if s == '':
258
+ self._value = None if self._allow_blank else self._value
259
+ else:
260
+ try:
261
+ self._value = s if self._value_format is None else self._fmt.parse(s, self._value_format)
262
+ except ValueError:
263
+ # keep prior value on parse failure
264
+ return
265
+
266
+ # Format the value for display
267
+ new_text = self._format_value(self._value)
268
+
269
+ if new_text != self.textsignal.get():
270
+ # temporarily silence CHANGE while normalizing text
271
+ fid = getattr(self, '_on_input_fid', None)
272
+ if fid:
273
+ try:
274
+ self.textsignal.unsubscribe(fid)
275
+ except TclError:
276
+ pass
277
+ self.textsignal.set(new_text)
278
+ if fid:
279
+ self._on_input_fid = self.textsignal.subscribe(self._handle_change)
280
+
281
+ def _on_locale_changed(self, event=None):
282
+ """Respond to global changes in locale by updating formatter and text"""
283
+ self._fmt = IntlFormatter(locale=MessageCatalog.locale())
284
+
285
+ # reformat current value with new locale + same value_format
286
+ if self._value is not None:
287
+ formatted_text = self._format_value(self._value)
288
+ self.textsignal.set(formatted_text)
289
+
290
+ @configure_delegate('text')
291
+ def _delegate_text(self, value=None):
292
+ if value is None:
293
+ return self.text()
294
+ else:
295
+ return self.text(value)
296
+
297
+ @configure_delegate('value')
298
+ def _delegate_value(self, value=None):
299
+ if value is None:
300
+ return self.value()
301
+ else:
302
+ return self.value(value)
303
+
304
+ @configure_delegate('value_format')
305
+ def _delegate_value_format(self, value: str):
306
+ """Get or set the value format pattern and reformat display.
307
+
308
+ Can be accessed via:
309
+ widget.configure(value_format='#,##0.00')
310
+ widget['value_format'] = '#,##0.00'
311
+ widget.cget('value_format')
312
+ """
313
+ if value is None:
314
+ return self._value_format
315
+
316
+ self._value_format = value
317
+ # Reformat current value with new format
318
+ if self._value is not None:
319
+ formatted_text = self._format_value(self._value)
320
+ self.textsignal.set(formatted_text)
321
+
322
+ @configure_delegate('allow_blank')
323
+ def _delegate_allow_blank(self, value: bool):
324
+ """Get or set whether blank input is allowed.
325
+
326
+ Can be accessed via:
327
+ widget.configure(allow_blank=True)
328
+ widget['allow_blank'] = True
329
+ widget.cget('allow_blank')
330
+ """
331
+ if value is None:
332
+ return self._allow_blank
333
+
334
+ self._allow_blank = bool(value)
335
+
336
+ def destroy(self):
337
+ """Clean up signal subscriptions and destroy the widget."""
338
+ if self._on_input_fid:
339
+ try:
340
+ self.textsignal.unsubscribe(self._on_input_fid)
341
+ except Exception:
342
+ pass # Ignore all errors during cleanup
343
+ self._on_input_fid = None
344
+ super().destroy()
@@ -0,0 +1,55 @@
1
+ """Primitive bootstack widgets."""
2
+
3
+ from .badge import Badge
4
+ from .button import Button
5
+ from .card import Card
6
+ from .checkbutton import CheckButton
7
+ from .checktoggle import CheckToggle
8
+ from .combobox import Combobox
9
+ from .entry import Entry
10
+ from .frame import Frame
11
+ from .gridframe import GridFrame
12
+ from .label import Label
13
+ from .labelframe import LabelFrame
14
+ from .menubutton import MenuButton
15
+ from .notebook import Notebook
16
+ from .packframe import PackFrame
17
+ from .panedwindow import PanedWindow
18
+ from .progressbar import Progressbar
19
+ from .radiobutton import RadioButton
20
+ from .radiotoggle import RadioToggle
21
+ from .scale import Scale
22
+ from .scrollbar import Scrollbar
23
+ from .separator import Separator
24
+ from .sizegrip import SizeGrip
25
+ from .spinbox import Spinbox
26
+ from .switch import Switch
27
+ from .treeview import TreeView
28
+
29
+ __all__ = [
30
+ "Badge",
31
+ "Button",
32
+ "Card",
33
+ "CheckButton",
34
+ "CheckToggle",
35
+ "Combobox",
36
+ "Entry",
37
+ "Frame",
38
+ "GridFrame",
39
+ "Label",
40
+ "LabelFrame",
41
+ "MenuButton",
42
+ "Notebook",
43
+ "PackFrame",
44
+ "PanedWindow",
45
+ "Progressbar",
46
+ "RadioButton",
47
+ "RadioToggle",
48
+ "Scale",
49
+ "Scrollbar",
50
+ "Separator",
51
+ "SizeGrip",
52
+ "Spinbox",
53
+ "Switch",
54
+ "TreeView",
55
+ ]
@@ -0,0 +1,44 @@
1
+ from typing_extensions import Unpack
2
+ from bootstack.widgets.primitives.label import Label, LabelKwargs
3
+ from bootstack.widgets.types import Master
4
+
5
+
6
+ class Badge(Label):
7
+ """bootstack wrapper for `ttk.Label` that renders with a badge style."""
8
+
9
+ def __init__(self, master: Master = None, **kwargs: Unpack[LabelKwargs]):
10
+ """Create a themed bootstack Badge.
11
+
12
+ Args:
13
+ master: Parent widget. If None, uses the default root window.
14
+
15
+ Other Parameters:
16
+ text (str): Text to display on the badge.
17
+ image (PhotoImage): Image to display.
18
+ icon (str | dict): Theme-aware icon spec handled by the style system.
19
+ icon_only (bool): If True, removes the additional padding reserved for label text.
20
+ compound (str): Placement of the image relative to text.
21
+ anchor (str): Alignment of the badge content within its area.
22
+ justify (str): How to justify multiple lines of text.
23
+ localize (bool | Literal['auto']): Determines the widget's localization mode.
24
+ value_format (str | dict): Format specification for the badge value.
25
+ padding (int | tuple): Extra space around the badge content.
26
+ width (int): Width of the badge in characters.
27
+ wraplength (int): Maximum width before wrapping text.
28
+ font (str | Font): Font for the badge text.
29
+ foreground (str): Text color.
30
+ background (str): Background color.
31
+ relief (str): Border style.
32
+ state (str): Widget state.
33
+ takefocus (bool): Whether the widget participates in focus traversal.
34
+ style (str): Explicit ttk style name (overrides accent/variant).
35
+ accent (str): Accent token for styling, e.g. 'primary', 'success', 'danger'.
36
+ variant (str): Shape of badge. 'pill' or 'square' (default).
37
+ surface (str): Optional surface token; otherwise inherited.
38
+ style_options (dict): Optional dict forwarded to the style builder.
39
+ """
40
+ kwargs.setdefault('ttk_class', 'TBadge')
41
+ kwargs.setdefault('anchor', 'center')
42
+ kwargs.setdefault('font', '-size 8')
43
+ kwargs.setdefault('variant', 'square')
44
+ super().__init__(master=master, **kwargs)
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from tkinter import ttk
4
+ from typing import Any, Callable, Literal, Optional, TypedDict, TYPE_CHECKING
5
+ from typing_extensions import Unpack
6
+
7
+ from bootstack.core.mixins.ttk_state import TtkStateMixin
8
+ from bootstack.core.mixins.widget import WidgetCapabilitiesMixin
9
+ from bootstack.widgets.internal.wrapper_base import TTKWrapperBase
10
+ from bootstack.widgets.mixins import IconMixin, TextSignalMixin, LocalizationMixin
11
+ from bootstack.widgets.types import Master
12
+
13
+ if TYPE_CHECKING:
14
+ from bootstack.core.signals import Signal
15
+
16
+
17
+ class ButtonKwargs(TypedDict, total=False):
18
+ # Standard ttk.Button options
19
+ text: Any
20
+ command: Optional[Callable[[], Any]]
21
+ image: Any
22
+ icon: Any
23
+ icon_only: bool
24
+ anchor: str
25
+ compound: Literal['text', 'image', 'top', 'bottom', 'left', 'right', 'center', 'none'] | str
26
+ padding: Any
27
+ width: int
28
+ underline: int
29
+ state: Literal['normal', 'active', 'disabled', 'readonly'] | str
30
+ takefocus: Any
31
+ localize: bool | Literal['auto']
32
+ style: str
33
+ class_: str
34
+ cursor: str
35
+ default: Any
36
+ name: str
37
+ textvariable: Any
38
+ textsignal: Signal[str]
39
+
40
+ # bootstack-specific extensions
41
+ bootstyle: str # DEPRECATED: Use accent and variant instead
42
+ accent: str
43
+ density: Literal['default', 'compact']
44
+ variant: str
45
+ surface: str
46
+ style_options: dict[str, Any]
47
+
48
+
49
+ class Button(LocalizationMixin, TextSignalMixin, IconMixin, TTKWrapperBase, WidgetCapabilitiesMixin, TtkStateMixin, ttk.Button):
50
+ """TTK Bootstrap Button
51
+
52
+ bootstack wrapper for `ttk.Button` with bootstyle and icon support.
53
+ """
54
+ _ttk_base = ttk.Button
55
+
56
+ def __init__(self, master: Master = None, **kwargs: Unpack[ButtonKwargs]) -> None:
57
+ """Create a themed bootstack Button.
58
+
59
+ Args:
60
+ master: Parent widget. If None, uses the default root window.
61
+
62
+ Other Parameters:
63
+ text (str): Text to display on the button.
64
+ textvariable (Variable): Tk variable linked to the button text.
65
+ textsignal (Signal[str]): Reactive Signal linked to the button text (auto-synced with textvariable).
66
+ command (Callable): Callable invoked when the button is pressed.
67
+ image (PhotoImage): Image to display on the button.
68
+ icon (str | dict): Optional icon spec integrated via the style system.
69
+ icon_only (bool): If true, removes the extra padding reserved for the text labels.
70
+ compound (str): Placement of the image relative to text (e.g., 'left').
71
+ padding (int | tuple): Extra space around the button content.
72
+ anchor (str): Determines how the content is aligned in the container. Combination of 'n', 's', 'e', 'w', or 'center' (default).
73
+ localize (bool | Literal['auto']): Determines the widgets localization mode.
74
+ width (int): Width of the button in characters.
75
+ underline (int): Index of the character to underline in `text`.
76
+ state (str): Widget state (e.g., 'normal', 'disabled').
77
+ takefocus (bool): Whether the widget accepts focus during traversal.
78
+ style (str): Explicit ttk style name to apply (overrides accent/variant).
79
+ accent (str): Accent token for styling, e.g. 'primary', 'danger', 'success'.
80
+ variant (str): Style variant, e.g. 'solid', 'outline', 'link', 'text'. Defaults to 'solid'.
81
+ density (str): The vertical and horizontal compactness of widget content, e.g. 'default', 'compact'.
82
+ bootstyle (str): DEPRECATED - Use `accent` and `variant` instead.
83
+ surface (str): Optional surface token to use for this button; if not provided, surface color is inherited from the parent.
84
+ style_options (dict): Optional dict forwarded to the style builder.
85
+ """
86
+ if 'bootstyle' not in kwargs:
87
+ kwargs.setdefault('variant', 'solid')
88
+ kwargs.update(style_options=self._capture_style_options(['icon_only', 'icon', 'anchor', 'density'], kwargs))
89
+ super().__init__(master, **kwargs)
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, TypedDict
4
+
5
+ from typing_extensions import Unpack
6
+
7
+ from bootstack.widgets.primitives.frame import Frame
8
+ from bootstack.widgets.types import Master
9
+
10
+
11
+ class CardKwargs(TypedDict, total=False):
12
+ # Standard ttk.Frame options
13
+ padding: Any
14
+ width: int
15
+ height: int
16
+ style: str
17
+ cursor: str
18
+ name: str
19
+ takefocus: bool
20
+ class_: str
21
+
22
+ # bootstack-specific extensions
23
+ accent: str
24
+ variant: str
25
+ surface: str
26
+ show_border: bool
27
+ style_options: dict[str, Any]
28
+ bootstyle: str # DEPRECATED: Use accent and variant instead
29
+
30
+
31
+ class Card(Frame):
32
+ """A convenience wrapper for Frame with card styling.
33
+
34
+ Card is a Frame with `surface='card'` and `show_border=True` by default,
35
+ providing an elevated container with a visible border for grouping content.
36
+
37
+ Example:
38
+ ```python
39
+ card = ttk.Card(app, padding=20)
40
+ ttk.Label(card, text="Card content").pack()
41
+ ```
42
+ """
43
+
44
+ def __init__(self, master: Master = None, **kwargs: Unpack[CardKwargs]) -> None:
45
+ """Create a themed Card container.
46
+
47
+ Args:
48
+ master: Parent widget. If None, uses the default root window.
49
+
50
+ Other Parameters:
51
+ padding (int | tuple): Extra padding inside the card. Default 16.
52
+ width (int): Requested width in pixels.
53
+ height (int): Requested height in pixels.
54
+ takefocus (bool): Widget accepts focus during keyboard traversal.
55
+ style (str): Explicit ttk style name (overrides accent/variant).
56
+ accent (str): Accent/color token for the card. Default 'card'.
57
+ variant (str): Style variant (if applicable).
58
+ surface (str): Surface token for the parent background.
59
+ show_border (bool): Draw a border around the card. Default True.
60
+ style_options (dict): Optional dict forwarded to the style builder.
61
+ """
62
+ if 'bootstyle' not in kwargs:
63
+ kwargs.setdefault('accent', 'card')
64
+ kwargs.setdefault('show_border', True)
65
+ kwargs.setdefault('padding', 16)
66
+ super().__init__(master, **kwargs)